Skip to content

Commit

Permalink
feat: add page to update webhook
Browse files Browse the repository at this point in the history
  • Loading branch information
rsbh committed Feb 5, 2025
1 parent 71f594c commit 14704de
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 77 deletions.
163 changes: 86 additions & 77 deletions ui/src/components/CustomField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import React, { CSSProperties } from "react";
import { Control, Controller, UseFormRegister } from "react-hook-form";
import { capitalizeFirstLetter } from "~/utils/helper";
import { MultiSelect } from "./multiselect";
import Skeleton from "react-loading-skeleton";

type CustomFieldNameProps = {
name: string;
isLoading?: boolean;
title?: string;
disabled?: boolean;
register: UseFormRegister<any>;
Expand All @@ -33,6 +35,7 @@ export const CustomFieldName = ({
style = {},
placeholder,
options = [],
isLoading = false,
...props
}: FormFieldProps &
CustomFieldNameProps &
Expand Down Expand Up @@ -64,86 +67,92 @@ export const CustomFieldName = ({
</FormMessage>
</Flex>
<FormControl asChild>
<Controller
defaultValue={props.defaultValue}
name={name}
control={control}
render={({ field }) => {
switch (variant) {
case "textarea": {
return (
<textarea
{...field}
placeholder={
placeholder ||
`Enter your ${title?.toLowerCase() || name}`
}
style={style}
/>
);
}
case "select": {
const { ref, ...rest } = field;
return (
<Select
{...rest}
onValueChange={(value: any) => field.onChange(value)}
>
<Select.Trigger
ref={ref}
style={{ height: "26px", width: "100%" }}
{isLoading ? (
<Skeleton />
) : (
<Controller
defaultValue={props.defaultValue}
name={name}
control={control}
render={({ field }) => {
switch (variant) {
case "textarea": {
return (
<textarea
{...field}
defaultValue={props?.defaultValue}
placeholder={
placeholder ||
`Enter your ${title?.toLowerCase() || name}`
}
style={style}
/>
);
}
case "select": {
const { ref, ...rest } = field;
return (
<Select
{...rest}
onValueChange={(value: any) => field.onChange(value)}
>
<Select.Value placeholder={`Select ${inputTitle}`} />
</Select.Trigger>
<Select.Content style={{ width: "320px" }}>
<Select.Group>
{options.map((opt) => (
<Select.Item key={opt.value} value={opt.value}>
{opt.label}
</Select.Item>
))}
</Select.Group>
</Select.Content>
</Select>
);
}
case "multiselect": {
const { onChange, value, ...rest } = field;
return (
<MultiSelect<string>
{...rest}
options={options}
onSelect={onChange}
selected={value}
/>
);
}
case "switch": {
const { onChange, value, ...rest } = field;
return (
<Switch
{...rest}
checked={value}
onCheckedChange={onChange}
/>
);
}
<Select.Trigger
ref={ref}
style={{ height: "26px", width: "100%" }}
>
<Select.Value placeholder={`Select ${inputTitle}`} />
</Select.Trigger>
<Select.Content style={{ width: "320px" }}>
<Select.Group>
{options.map((opt) => (
<Select.Item key={opt.value} value={opt.value}>
{opt.label}
</Select.Item>
))}
</Select.Group>
</Select.Content>
</Select>
);
}
case "multiselect": {
const { onChange, value, ...rest } = field;
return (
<MultiSelect<string>
{...rest}
options={options}
onSelect={onChange}
selected={value || props?.defaultValue}
/>
);
}
case "switch": {
const { onChange, value, ...rest } = field;
return (
<Switch
{...rest}
defaultChecked={props?.defaultChecked}
checked={value}
onCheckedChange={onChange}
/>
);
}

default: {
return (
<TextField
{...field}
placeholder={
placeholder ||
`Enter your ${title?.toLowerCase() || name}`
}
disabled={disabled}
/>
);
default: {
return (
<TextField
{...field}
placeholder={
placeholder ||
`Enter your ${title?.toLowerCase() || name}`
}
disabled={disabled}
/>
);
}
}
}
}}
/>
}}
/>
)}
</FormControl>
</Flex>
</FormField>
Expand Down
198 changes: 198 additions & 0 deletions ui/src/containers/webhooks/update/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { useCallback, useEffect, useState } from "react";
import { Button, Flex, Sheet, Text } from "@raystack/apsara";
import { useNavigate, useParams } from "react-router-dom";
import { SheetHeader } from "~/components/sheet/header";
import * as z from "zod";
import { FormProvider, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { Form, FormSubmit } from "@radix-ui/react-form";
import { CustomFieldName } from "~/components/CustomField";
import events from "~/utils/webhook_events";
import { SheetFooter } from "~/components/sheet/footer";
import { useFrontier } from "@raystack/frontier/react";
import { V1Beta1Webhook, V1Beta1WebhookRequestBody } from "@raystack/frontier";
import { toast } from "sonner";

const UpdateWebhookSchema = z.object({
url: z.string().trim().url(),
description: z
.string()
.trim()
.min(3, { message: "Must be 10 or more characters long" }),
state: z.boolean().default(false),
subscribed_events: z.array(z.string()).default([]),
});

export type UpdateWebhook = z.infer<typeof UpdateWebhookSchema>;

export default function UpdateWebhooks() {
const navigate = useNavigate();
const { client } = useFrontier();

const [isSubmitting, setIsSubmitting] = useState(false);

const [isWebhookLoading, setIsWebhookLoading] = useState(false);
const [webhook, setWebhook] = useState<V1Beta1Webhook>();

const { webhookId = "" } = useParams();

const onClose = useCallback(() => {
navigate("/webhooks");
}, [navigate]);

const methods = useForm<UpdateWebhook>({
resolver: zodResolver(UpdateWebhookSchema),
defaultValues: {},
});

const onSubmit = async (data: UpdateWebhook) => {
console.log("called");
try {
setIsSubmitting(true);
const body: V1Beta1WebhookRequestBody = {
...data,
state: data.state ? "enabled" : "disabled",
};
const resp = await client?.adminServiceUpdateWebhook(webhookId, {
body,
});

if (resp?.data?.webhook) {
toast.success("Webhook updated");
}
} catch (err) {
toast.error("Something went wrong");
} finally {
setIsSubmitting(false);
}
};

useEffect(() => {
async function getWebhookDetails(id: string) {
try {
setIsWebhookLoading(true);
const resp = await client?.adminServiceListWebhooks();
const webhooks = resp?.data?.webhooks || [];
const webhookData = webhooks?.find((wb) => wb?.id === id);
setWebhook(webhookData);
methods?.reset({
...webhookData,
state: webhookData?.state === "enabled",
});
} catch (err) {
console.error(err);
} finally {
setIsWebhookLoading(false);
}
}

if (webhookId) {
getWebhookDetails(webhookId);
}
}, [client, methods, webhookId]);

return (
<Sheet open={true}>
<Sheet.Content
side="right"
// @ts-ignore
style={{
width: "30vw",
padding: 0,
borderRadius: "var(--pd-8)",
boxShadow: "var(--shadow-sm)",
}}
close={false}
>
<FormProvider {...methods}>
<Form onSubmit={methods.handleSubmit(onSubmit)}>
<SheetHeader
title="Update Webhook"
onClick={onClose}
data-test-id="admin-ui-update-webhook-close-btn"
/>
<Flex direction="column" gap="large" style={styles.main}>
<CustomFieldName
name="url"
defaultValue={webhook?.url}
register={methods.register}
control={methods.control}
variant="textarea"
style={{ width: "100%" }}
isLoading={isWebhookLoading}
/>
<CustomFieldName
name="description"
defaultValue={webhook?.description}
register={methods.register}
control={methods.control}
variant="textarea"
style={{ width: "100%" }}
isLoading={isWebhookLoading}
/>
<CustomFieldName
name="subscribed_events"
defaultValue={webhook?.subscribed_events}
register={methods.register}
control={methods.control}
variant="multiselect"
options={events.map((e) => ({ label: e, value: e }))}
isLoading={isWebhookLoading}
/>
<CustomFieldName
name="state"
defaultChecked={webhook?.state === "enabled"}
register={methods.register}
control={methods.control}
variant="switch"
isLoading={isWebhookLoading}
/>
</Flex>
<SheetFooter>
<FormSubmit asChild>
<Button
variant="primary"
style={{ height: "inherit" }}
disabled={isSubmitting || isWebhookLoading}
data-test-id="admin-ui-submit-btn"
>
<Text
size={4}
style={{ color: "var(--foreground-inverted)" }}
>
{isSubmitting ? "Updating..." : "Update Webhook"}
</Text>
</Button>
</FormSubmit>
</SheetFooter>
</Form>
</FormProvider>
</Sheet.Content>
</Sheet>
);
}

const styles = {
main: {
padding: "32px",
margin: 0,
height: "calc(100vh - 125px)",
overflow: "auto",
},
formfield: {
width: "80%",
marginBottom: "40px",
},
select: {
height: "32px",
borderRadius: "8px",
padding: "8px",
border: "none",
backgroundColor: "transparent",
"&:active,&:focus": {
border: "none",
outline: "none",
boxShadow: "none",
},
},
};
Loading

0 comments on commit 14704de

Please sign in to comment.