Skip to content

Commit

Permalink
feat: add confirmation on webhook save
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanhopperlowe committed Nov 20, 2024
1 parent 0cbad1b commit 5a8fa84
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 31 deletions.
6 changes: 3 additions & 3 deletions ui/admin/app/components/composed/ConfirmationDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ export function ConfirmationDialog({
children?: ReactNode;
title: ReactNode;
description?: ReactNode;
onConfirm: () => void;
onCancel?: () => void;
onConfirm: (e: React.MouseEvent<HTMLButtonElement>) => void;
onCancel?: (e: React.MouseEvent<HTMLButtonElement>) => void;
confirmProps?: Omit<Partial<ComponentProps<typeof Button>>, "onClick">;
closeOnConfirm?: boolean;
}) {
return (
<Dialog {...dialogProps}>
{children && <DialogTrigger asChild>{children}</DialogTrigger>}

<DialogContent>
<DialogContent onClick={(e) => e.stopPropagation()}>
<DialogTitle>{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
<DialogFooter>
Expand Down
1 change: 1 addition & 0 deletions ui/admin/app/components/ui/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const linkVariants = cva("", {
as: {
button: cn(ButtonClasses.base, "flex flex-row items-center gap-2"),
default: "text-primary underline-offset-4 underline",
div: "",
},
buttonVariant: ButtonClasses.variants.variant,
buttonSize: ButtonClasses.variants.size,
Expand Down
75 changes: 75 additions & 0 deletions ui/admin/app/components/webhooks/WebhookConfirmation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { $path } from "remix-routes";

import { Webhook } from "~/lib/model/webhooks";

import { TypographyP } from "~/components/Typography";
import { CopyText } from "~/components/composed/CopyText";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "~/components/ui/dialog";
import { Link } from "~/components/ui/link";

export type WebhookConfirmationProps = {
webhook: Webhook;
token?: string;
secret: string;
type?: "github";
};

export const WebhookConfirmation = ({
webhook,
token: _token,
secret,
type: _ = "github",
}: WebhookConfirmationProps) => {
return (
<Dialog open>
<DialogContent className="max-w-[700px]">
<DialogHeader>
<DialogTitle>Webhook Saved</DialogTitle>
</DialogHeader>

<DialogDescription>
Your webhook has been saved in Otto. Make sure to copy
payload url and secret to your webhook provider.
</DialogDescription>

<DialogDescription>
This information will not be shown again.
</DialogDescription>

<div className="flex items-center justify-between">
<TypographyP>Payload URL: </TypographyP>
<CopyText
text={webhook.links?.invoke ?? ""}
className="min-w-fit"
/>
</div>

<div className="flex items-center justify-between">
<TypographyP>Secret: </TypographyP>
<CopyText
className="min-w-fit"
displayText={secret}
text={secret ?? ""}
/>
</div>

<DialogFooter>
<Link
as="button"
className="w-full"
to={$path("/webhooks")}
>
Continue
</Link>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
23 changes: 18 additions & 5 deletions ui/admin/app/components/webhooks/WebhookFormContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { createContext, useContext, useEffect, useMemo } from "react";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { UseFormHandleSubmit, useForm, useFormContext } from "react-hook-form";
import { toast } from "sonner";
import { mutate } from "swr";
Expand All @@ -9,10 +9,13 @@ import { Webhook, WebhookFormType, WebhookSchema } from "~/lib/model/webhooks";
import { WebhookApiService } from "~/lib/service/api/webhookApiService";

import { Form } from "~/components/ui/form";
import {
WebhookConfirmation,
WebhookConfirmationProps,
} from "~/components/webhooks/WebhookConfirmation";
import { useAsync } from "~/hooks/useAsync";

export type WebhookFormContextProps = {
onSuccess?: () => void;
webhook?: Webhook;
};

Expand All @@ -35,10 +38,12 @@ const EditSchema = WebhookSchema.omit({ token: true }).extend({
export function WebhookFormContextProvider({
children,
webhook,
onSuccess,
}: WebhookFormContextProps & { children: React.ReactNode }) {
const webhookId = webhook?.id;

const [webhookConfirmation, showWebhookConfirmation] =
useState<WebhookConfirmationProps | null>(null);

const updateWebhook = useAsync(WebhookApiService.updateWebhook, {
onSuccess: () => {
toast.success("Webhook updated");
Expand Down Expand Up @@ -74,7 +79,7 @@ export function WebhookFormContextProvider({
}, [defaultValues, form]);

const handleSubmit = form.handleSubmit(async (values) => {
const { error } = webhookId
const { error, data } = webhookId
? await updateWebhook.executeAsync(webhookId, values)
: await createWebhook.executeAsync(values);

Expand All @@ -86,7 +91,11 @@ export function WebhookFormContextProvider({
}

mutate(WebhookApiService.getWebhooks.key());
onSuccess?.();
showWebhookConfirmation({
webhook: data,
secret: values.secret,
token: values.token,
});
});

return (
Expand All @@ -103,6 +112,10 @@ export function WebhookFormContextProvider({
}}
>
{children}

{webhookConfirmation && (
<WebhookConfirmation {...webhookConfirmation} />
)}
</Context.Provider>
</Form>
);
Expand Down
8 changes: 6 additions & 2 deletions ui/admin/app/lib/model/primitives.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
export type Metadata = Record<string, string>;
export type MetaLinks = Record<string, string>;

export type EntityMeta<
TMetadata extends Record<string, string> = Record<string, string>,
TMetadata extends Metadata = Metadata,
TLinks extends MetaLinks = MetaLinks,
> = {
created: string;
deleted?: string; // date
id: string;
links: Record<string, string>;
links?: TLinks;
metadata?: TMetadata;
type?: string;
};
6 changes: 4 additions & 2 deletions ui/admin/app/lib/model/webhooks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";

import { EntityMeta } from "~/lib/model/primitives";
import { EntityMeta, MetaLinks, Metadata } from "~/lib/model/primitives";

export type WebhookBase = {
name: string;
Expand All @@ -18,7 +18,9 @@ export type WebhookDetail = WebhookBase & {
hasToken?: boolean;
};

export type Webhook = EntityMeta & WebhookDetail;
type WebhookLinks = { invoke: string } & MetaLinks;

export type Webhook = EntityMeta<Metadata, WebhookLinks> & WebhookDetail;

type WebhookPayload = WebhookBase & {
token: Nullish<string>;
Expand Down
15 changes: 2 additions & 13 deletions ui/admin/app/routes/_auth.webhooks.$webhook.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
ClientLoaderFunctionArgs,
useLoaderData,
useNavigate,
} from "@remix-run/react";
import { $path } from "remix-routes";
import { ClientLoaderFunctionArgs, useLoaderData } from "@remix-run/react";
import useSWR, { preload } from "swr";

import { WebhookApiService } from "~/lib/service/api/webhookApiService";
Expand Down Expand Up @@ -31,17 +26,11 @@ export async function clientLoader({

export default function Webhook() {
const { webhookId } = useLoaderData<typeof clientLoader>();
const navigate = useNavigate();

const { data: webhook } = useSWR(
WebhookApiService.getWebhookById.key(webhookId),
({ id }) => WebhookApiService.getWebhookById(id)
);

return (
<WebhookForm
webhook={webhook}
onSuccess={() => navigate($path("/webhooks"))}
/>
);
return <WebhookForm webhook={webhook} />;
}
7 changes: 1 addition & 6 deletions ui/admin/app/routes/_auth.webhooks.create.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import { useNavigate } from "@remix-run/react";
import { $path } from "remix-routes";

import { WebhookForm } from "~/components/webhooks/WebhookForm";

export default function CreateWebhookPage() {
const navigate = useNavigate();

return <WebhookForm onSuccess={() => navigate($path("/webhooks"))} />;
return <WebhookForm />;
}

0 comments on commit 5a8fa84

Please sign in to comment.