diff --git a/web/apps/admin/configs.dev.json b/web/apps/admin/configs.dev.json index d858368e7..a5678293b 100644 --- a/web/apps/admin/configs.dev.json +++ b/web/apps/admin/configs.dev.json @@ -3,9 +3,6 @@ "app_url": "localhost:5173", "token_product_id": "token", "organization_types": [], - "webhooks": { - "enable_delete": false - }, "terminology": { "organization": { "singular": "Organization", diff --git a/web/apps/admin/src/pages/webhooks/WebhooksPage.tsx b/web/apps/admin/src/pages/webhooks/WebhooksPage.tsx index b919da73b..916859a84 100644 --- a/web/apps/admin/src/pages/webhooks/WebhooksPage.tsx +++ b/web/apps/admin/src/pages/webhooks/WebhooksPage.tsx @@ -1,26 +1,6 @@ -import { useContext } from "react"; -import { useMatch, useParams, useNavigate } from "react-router-dom"; import { WebhooksView } from "@raystack/frontier/admin"; -import { AppContext } from "~/contexts/App"; import WebhooksIcon from "~/assets/icons/webhooks.svg?react"; export default function WebhooksPage() { - const { config } = useContext(AppContext); - const { webhookId } = useParams(); - const navigate = useNavigate(); - const isCreate = useMatch("/webhooks/create"); - - const enableDelete = config?.webhooks?.enable_delete ?? false; - - return ( - navigate("/webhooks")} - onSelectWebhook={(id: string) => navigate(`/webhooks/${encodeURIComponent(id)}`)} - onOpenCreate={() => navigate("/webhooks/create")} - enableDelete={enableDelete} - icon={} - /> - ); + return } />; } diff --git a/web/apps/admin/src/routes.tsx b/web/apps/admin/src/routes.tsx index 6f0e454e2..31c2457ad 100644 --- a/web/apps/admin/src/routes.tsx +++ b/web/apps/admin/src/routes.tsx @@ -112,10 +112,7 @@ export default memo(function AppRoutes() { } /> } /> - }> - } /> - } /> - + } /> } /> diff --git a/web/apps/admin/src/utils/constants.ts b/web/apps/admin/src/utils/constants.ts index ded60ed1a..cc78e5859 100644 --- a/web/apps/admin/src/utils/constants.ts +++ b/web/apps/admin/src/utils/constants.ts @@ -13,10 +13,6 @@ export const SUBSCRIPTION_STATUSES = [ { label: "Ended", value: "ended" }, ]; -export interface WebhooksConfig { - enable_delete: boolean; -} - export interface EntityTerminologies { singular: string; plural: string; @@ -37,7 +33,6 @@ export interface Config { app_url?: string; token_product_id?: string; organization_types?: string[]; - webhooks?: WebhooksConfig; terminology?: AdminTerminologyConfig; } @@ -55,9 +50,6 @@ export const defaultConfig: Config = { app_url: "example.com", token_product_id: DEFAULT_TOKEN_PRODUCT_NAME, organization_types: [], - webhooks: { - enable_delete: false, - }, terminology: defaultTerminology, }; diff --git a/web/sdk/admin/views/webhooks/webhooks/columns.tsx b/web/sdk/admin/views/webhooks/webhooks/columns.tsx index 46c45c0a8..38339ce2a 100644 --- a/web/sdk/admin/views/webhooks/webhooks/columns.tsx +++ b/web/sdk/admin/views/webhooks/webhooks/columns.tsx @@ -1,10 +1,4 @@ -import { DotsVerticalIcon, TrashIcon, UpdateIcon } from "@radix-ui/react-icons"; -import { - Menu, - Flex, - Text, - type DataTableColumnDef, -} from "@raystack/apsara"; +import { Text, type DataTableColumnDef } from "@raystack/apsara"; import styles from "./webhooks.module.css"; import { type Webhook } from "@raystack/proton/frontier"; import { @@ -13,19 +7,8 @@ import { type TimeStamp, } from "../../../utils/connect-timestamp"; import dayjs from "dayjs"; -import { useState } from "react"; -import type { useMutation } from "@connectrpc/connect-query"; -import { DeleteWebhookDialog } from "./delete"; - -interface getColumnsOptions { - openEditPage: (id: string) => void; - deleteWebhookMutation: ReturnType; - enableDelete: boolean; -} -export const getColumns: ( - opt: getColumnsOptions, -) => DataTableColumnDef[] = ({ openEditPage, deleteWebhookMutation, enableDelete }) => { +export const getColumns: () => DataTableColumnDef[] = () => { return [ { header: "Description", @@ -61,64 +44,5 @@ export const getColumns: ( return {date}; }, }, - { - header: "Action", - accessorKey: "id", - classNames: { cell: styles.actionColumn, header: styles.actionColumn }, - cell: ({ getValue, row }) => { - const ActionCell = () => { - const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); - const webhookId = getValue() as string; - const webhook = row.original; - - return ( - <> - {/* @ts-ignore */} - - } - /> - - - - openEditPage(webhookId)} - > - - Update - - - - enableDelete && setIsDeleteDialogOpen(true)} - > - - Delete - - - - - - - - - ); - }; - - return ; - }, - }, ]; }; diff --git a/web/sdk/admin/views/webhooks/webhooks/create/index.tsx b/web/sdk/admin/views/webhooks/webhooks/create/index.tsx deleted file mode 100644 index f6dbfffce..000000000 --- a/web/sdk/admin/views/webhooks/webhooks/create/index.tsx +++ /dev/null @@ -1,192 +0,0 @@ -import { useCallback } from "react"; -import { Button, Flex, Drawer, toastManager } from "@raystack/apsara"; -import { SheetHeader } from "../../../../components/SheetHeader"; -import { SheetFooter } from "../../../../components/SheetFooter"; -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 { useMutation } from "@connectrpc/connect-query"; -import { handleConnectError } from "~/utils/error"; -import { - AdminServiceQueries, - type WebhookRequestBody, - WebhookRequestBodySchema, -} from "@raystack/proton/frontier"; -import { create } from "@bufbuild/protobuf"; -import { useWebhookQueries } from "../hooks/useWebhookQueries"; - -const NewWebookSchema = z.object({ - url: z.string().trim().url(), - description: z - .string() - .trim() - .min(3, { message: "Must be 3 or more characters long" }), - state: z.boolean().default(false), - subscribed_events: z.array(z.string()).default([]), -}); - -export type NewWebhook = z.infer; - -export type CreateWebhooksProps = { - open?: boolean; - onClose?: () => void; -}; - -export default function CreateWebhooks({ open = false, onClose: onCloseProp }: CreateWebhooksProps = {}) { - const { invalidateWebhooksList } = useWebhookQueries(); - - const onOpenChange = useCallback(() => { - onCloseProp?.(); - }, [onCloseProp]); - - const { mutateAsync: createWebhook, isPending: isSubmitting } = useMutation( - AdminServiceQueries.createWebhook, - ); - - const methods = useForm({ - resolver: zodResolver(NewWebookSchema), - defaultValues: { - url: "", - description: "", - state: false, - subscribed_events: [], - }, - }); - - const onSubmit = async (data: NewWebhook) => { - try { - const body: WebhookRequestBody = create(WebhookRequestBodySchema, { - url: data.url, - description: data.description, - state: data.state ? "enabled" : "disabled", - subscribedEvents: data.subscribed_events || [], - headers: {}, - }); - - const resp = await createWebhook({ body }); - - if (resp?.webhook) { - toastManager.add({ title: "Webhook created", type: "success" }); - await invalidateWebhooksList(); - methods.reset(); - onOpenChange(); - } else { - toastManager.add({ - title: "Something went wrong", - description: "Webhook was not created. Please try again.", - type: "error", - }); - } - } catch (err) { - console.error("Failed to create webhook:", err); - handleConnectError(err, { - PermissionDenied: () => - toastManager.add({ - title: "You don't have permission to perform this action", - type: "error", - }), - Default: (e) => - toastManager.add({ - title: "Something went wrong", - description: e.rawMessage, - type: "error", - }), - }); - } - }; - - return ( - !open && onCloseProp?.()}> - - -
- - - - - ({ label: e, value: e }))} - /> - - - - - - - - -
-
-
- ); -} - -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", - }, - }, -}; diff --git a/web/sdk/admin/views/webhooks/webhooks/delete/index.tsx b/web/sdk/admin/views/webhooks/webhooks/delete/index.tsx deleted file mode 100644 index 5b4484118..000000000 --- a/web/sdk/admin/views/webhooks/webhooks/delete/index.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { AlertDialog, Button, toastManager } from "@raystack/apsara"; -import type { useMutation } from "@connectrpc/connect-query"; -import { handleConnectError } from "~/utils/error"; - -interface DeleteWebhookDialogProps { - isOpen: boolean; - onOpenChange: (open: boolean) => void; - webhookId: string; - webhookDescription?: string; - deleteWebhookMutation: ReturnType; -} - -export function DeleteWebhookDialog({ - isOpen, - onOpenChange, - webhookId, - webhookDescription, - deleteWebhookMutation, -}: DeleteWebhookDialogProps) { - const handleDelete = async () => { - try { - await deleteWebhookMutation.mutateAsync({ id: webhookId }); - toastManager.add({ title: "Webhook deleted", type: "success" }); - onOpenChange(false); - } catch (err) { - console.error("Failed to delete webhook:", err); - handleConnectError(err, { - PermissionDenied: () => - toastManager.add({ - title: "You don't have permission to perform this action", - type: "error", - }), - Default: (e) => - toastManager.add({ - title: "Failed to delete webhook", - description: e.rawMessage, - type: "error", - }), - }); - } - }; - - return ( - - - - Delete Webhook - - Are you sure you want to delete this webhook - {webhookDescription ? ` "${webhookDescription}"` : ""}? This action - cannot be undone. - - - - - Cancel - - } - /> - - - - - ); -} diff --git a/web/sdk/admin/views/webhooks/webhooks/hooks/useWebhookQueries.ts b/web/sdk/admin/views/webhooks/webhooks/hooks/useWebhookQueries.ts index 488330535..6c94205b1 100644 --- a/web/sdk/admin/views/webhooks/webhooks/hooks/useWebhookQueries.ts +++ b/web/sdk/admin/views/webhooks/webhooks/hooks/useWebhookQueries.ts @@ -1,11 +1,7 @@ -import { createConnectQueryKey, useTransport, useQuery, useMutation } from "@connectrpc/connect-query"; +import { useQuery } from "@connectrpc/connect-query"; import { AdminServiceQueries } from "@raystack/proton/frontier"; -import { useQueryClient } from "@tanstack/react-query"; export function useWebhookQueries() { - const queryClient = useQueryClient(); - const transport = useTransport(); - const listWebhooks = useQuery( AdminServiceQueries.listWebhooks, {}, @@ -15,26 +11,7 @@ export function useWebhookQueries() { }, ); - const invalidateWebhooksList = async () => { - await queryClient.invalidateQueries({ - queryKey: createConnectQueryKey({ - schema: AdminServiceQueries.listWebhooks, - transport, - input: {}, - cardinality: "finite", - }), - }); - }; - - const deleteWebhookMutation = useMutation(AdminServiceQueries.deleteWebhook, { - onSuccess: () => { - invalidateWebhooksList(); - }, - }); - return { listWebhooks, - invalidateWebhooksList, - deleteWebhookMutation, }; } diff --git a/web/sdk/admin/views/webhooks/webhooks/index.tsx b/web/sdk/admin/views/webhooks/webhooks/index.tsx index ccb543543..230b350e7 100644 --- a/web/sdk/admin/views/webhooks/webhooks/index.tsx +++ b/web/sdk/admin/views/webhooks/webhooks/index.tsx @@ -1,56 +1,23 @@ -import { Button, Flex, DataTable, EmptyState } from "@raystack/apsara"; -import { useCallback, type ReactNode } from "react"; +import { Flex, DataTable, EmptyState } from "@raystack/apsara"; +import { type ReactNode } from "react"; import { getColumns } from "./columns"; import styles from "./webhooks.module.css"; import { useWebhookQueries } from "./hooks/useWebhookQueries"; -import { ExclamationTriangleIcon, PlusIcon } from "@radix-ui/react-icons"; +import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import { PageHeader } from "../../../components/PageHeader"; -import CreateWebhooks from "./create"; -import UpdateWebhooks from "./update"; export type WebhooksViewProps = { - /** When set, opens the update panel for this webhook. */ - selectedWebhookId?: string; - /** When true, opens the create webhook panel. */ - createOpen?: boolean; - /** Called when the detail/create panel is closed. */ - onCloseDetail?: () => void; - /** Called when a user clicks a webhook row. Use to update the URL or local state. */ - onSelectWebhook?: (id: string) => void; - /** Called when the "Create" button is clicked. Use to update the URL or open the create panel. */ - onOpenCreate?: () => void; - /** When true, shows the delete option for webhooks. Defaults to `false`. */ - enableDelete?: boolean; /** Icon rendered in the page header next to the title. */ icon?: ReactNode; }; -export default function WebhooksView({ - selectedWebhookId, - createOpen, - onCloseDetail, - onSelectWebhook, - onOpenCreate, - enableDelete = false, - icon, -}: WebhooksViewProps = {}) { +export default function WebhooksView({ icon }: WebhooksViewProps = {}) { const { - listWebhooks: { - data: webhooksResponse, - isLoading, - error, - isError, - }, - deleteWebhookMutation, + listWebhooks: { data: webhooksResponse, isLoading, error, isError }, } = useWebhookQueries(); const webhooks = webhooksResponse?.webhooks || []; - const openEditPage = useCallback( - (id: string) => (onSelectWebhook ? onSelectWebhook(id) : undefined), - [onSelectWebhook] - ); - if (isError) { console.error("ConnectRPC Error:", error); return ( @@ -65,53 +32,29 @@ export default function WebhooksView({ ); } - const columns = getColumns({ - openEditPage: (id) => { - openEditPage(id); - }, - deleteWebhookMutation, - enableDelete, - }); + const columns = getColumns(); return ( - <> - - - - - - - - - - - - + + + + + + + + ); } diff --git a/web/sdk/admin/views/webhooks/webhooks/update/index.tsx b/web/sdk/admin/views/webhooks/webhooks/update/index.tsx deleted file mode 100644 index 3da2b4692..000000000 --- a/web/sdk/admin/views/webhooks/webhooks/update/index.tsx +++ /dev/null @@ -1,220 +0,0 @@ -import { useCallback, useEffect } from "react"; -import { Button, Flex, Drawer, toastManager } from "@raystack/apsara"; -import { SheetHeader } from "../../../../components/SheetHeader"; -import { SheetFooter } from "../../../../components/SheetFooter"; -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 { useMutation } from "@connectrpc/connect-query"; -import { handleConnectError } from "~/utils/error"; -import { - AdminServiceQueries, - type WebhookRequestBody, - WebhookRequestBodySchema, -} from "@raystack/proton/frontier"; -import { create } from "@bufbuild/protobuf"; -import { useWebhookQueries } from "../hooks/useWebhookQueries"; - -const UpdateWebhookSchema = z.object({ - url: z.string().trim().url(), - description: z - .string() - .trim() - .min(3, { message: "Must be 3 or more characters long" }), - state: z.boolean().default(false), - subscribed_events: z.array(z.string()).default([]), -}); - -export type UpdateWebhook = z.infer; - -export type UpdateWebhooksProps = { - open?: boolean; - webhookId?: string; - onClose?: () => void; -}; - -export default function UpdateWebhooks({ open = false, webhookId: webhookIdProp, onClose: onCloseProp }: UpdateWebhooksProps = {}) { - const webhookId = webhookIdProp ?? ""; - - const { - listWebhooks: { - data: webhooksResponse, - isLoading: isWebhookLoading, - }, - invalidateWebhooksList, - } = useWebhookQueries(); - - const onClose = useCallback(() => { - onCloseProp?.(); - }, [onCloseProp]); - - const methods = useForm({ - resolver: zodResolver(UpdateWebhookSchema), - defaultValues: {}, - }); - - const webhook = webhooksResponse?.webhooks?.find( - (wb) => wb?.id === webhookId, - ); - - const { mutateAsync: updateWebhook, isPending: isSubmitting } = useMutation( - AdminServiceQueries.updateWebhook, - ); - - const onSubmit = async (data: UpdateWebhook) => { - try { - const body: WebhookRequestBody = create(WebhookRequestBodySchema, { - url: data.url, - description: data.description, - state: data.state ? "enabled" : "disabled", - subscribedEvents: data.subscribed_events || [], - headers: {}, - }); - - const resp = await updateWebhook({ - id: webhookId, - body, - }); - - if (resp?.webhook) { - toastManager.add({ title: "Webhook updated", type: "success" }); - await invalidateWebhooksList(); - onClose(); - } - } catch (err) { - console.error("Failed to update webhook:", err); - handleConnectError(err, { - PermissionDenied: () => - toastManager.add({ - title: "You don't have permission to perform this action", - type: "error", - }), - Default: (e) => - toastManager.add({ - title: "Something went wrong", - description: e.rawMessage, - type: "error", - }), - }); - } - }; - - useEffect(() => { - if (webhook) { - methods.reset({ - url: webhook.url, - description: webhook.description, - subscribed_events: webhook.subscribedEvents || [], - state: webhook.state === "enabled", - }); - } - }, [webhook, methods.reset]); - - return ( - !open && onClose()}> - - -
- - - - - ({ label: e, value: e }))} - isLoading={isWebhookLoading} - /> - - - - - - - - -
-
-
- ); -} - -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", - }, - }, -}; diff --git a/web/sdk/admin/views/webhooks/webhooks/webhooks.module.css b/web/sdk/admin/views/webhooks/webhooks/webhooks.module.css index 1f1a4f6b2..ffa3a1d04 100644 --- a/web/sdk/admin/views/webhooks/webhooks/webhooks.module.css +++ b/web/sdk/admin/views/webhooks/webhooks/webhooks.module.css @@ -14,10 +14,6 @@ padding-left: var(--rs-space-7); } -.actionColumn { - width: 80px; -} - .stateColumn { width: 100px; } @@ -31,9 +27,4 @@ border-bottom: 1px solid var(--rs-color-border-base-primary); display: flex; align-items: center; -} - -.deleteMenuItem { - padding: var(--rs-space-4); - color: var(--rs-color-foreground-danger-primary); } \ No newline at end of file