chore: ui updates
This commit is contained in:
@@ -13,6 +13,7 @@ import { Button } from '@documenso/ui/primitives/button';
|
|||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
@@ -24,7 +25,7 @@ import { Switch } from '@documenso/ui/primitives/switch';
|
|||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
import { SettingsHeader } from '~/components/(dashboard)/settings/layout/header';
|
import { SettingsHeader } from '~/components/(dashboard)/settings/layout/header';
|
||||||
import { MultiSelectCombobox } from '~/components/(dashboard)/settings/webhooks/multiselect-combobox';
|
import { TriggerMultiSelectCombobox } from '~/components/(dashboard)/settings/webhooks/trigger-multiselect-combobox';
|
||||||
|
|
||||||
const ZEditWebhookFormSchema = ZEditWebhookMutationSchema.omit({ id: true });
|
const ZEditWebhookFormSchema = ZEditWebhookMutationSchema.omit({ id: true });
|
||||||
|
|
||||||
@@ -95,36 +96,77 @@ export default function WebhookPage({ params }: WebhookPageOptions) {
|
|||||||
)}
|
)}
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||||
<fieldset className="flex h-full flex-col gap-y-6" disabled={form.formState.isSubmitting}>
|
<fieldset
|
||||||
<FormField
|
className="flex h-full max-w-xl flex-col gap-y-6"
|
||||||
control={form.control}
|
disabled={form.formState.isSubmitting}
|
||||||
name="webhookUrl"
|
>
|
||||||
render={({ field }) => (
|
<div className="flex flex-col-reverse gap-4 md:flex-row">
|
||||||
<FormItem>
|
<FormField
|
||||||
<FormLabel htmlFor="webhookUrl">Webhook URL</FormLabel>
|
control={form.control}
|
||||||
<Input {...field} id="webhookUrl" type="text" />
|
name="webhookUrl"
|
||||||
<FormMessage />
|
render={({ field }) => (
|
||||||
</FormItem>
|
<FormItem className="flex-1">
|
||||||
)}
|
<FormLabel required>Webhook URL</FormLabel>
|
||||||
/>
|
<FormControl>
|
||||||
|
<Input className="bg-background" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
The URL for Documenso to send webhook events to.
|
||||||
|
</FormDescription>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="enabled"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Enabled</FormLabel>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
className="bg-background"
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="eventTriggers"
|
name="eventTriggers"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<FormItem className="flex flex-col">
|
<FormItem className="flex flex-col gap-2">
|
||||||
<FormLabel required>Event triggers</FormLabel>
|
<FormLabel required>Triggers</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<MultiSelectCombobox
|
<TriggerMultiSelectCombobox
|
||||||
listValues={value}
|
listValues={value}
|
||||||
onChange={(values: string[]) => {
|
onChange={(values: string[]) => {
|
||||||
onChange(values);
|
onChange(values);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
The events that will trigger a webhook to be sent to your URL.
|
||||||
|
</FormDescription>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="secret"
|
name="secret"
|
||||||
@@ -134,28 +176,16 @@ export default function WebhookPage({ params }: WebhookPageOptions) {
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<PasswordInput className="bg-background" {...field} value={field.value ?? ''} />
|
<PasswordInput className="bg-background" {...field} value={field.value ?? ''} />
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
A secret that will be sent to your URL so you can verify that the request has
|
||||||
|
been sent by Documenso.
|
||||||
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FormField
|
|
||||||
control={form.control}
|
|
||||||
name="enabled"
|
|
||||||
render={({ field }) => (
|
|
||||||
<FormItem className="flex items-center gap-x-2">
|
|
||||||
<FormLabel className="mt-2">Active</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
className="bg-background"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||||
Update webhook
|
Update webhook
|
||||||
|
|||||||
@@ -2,16 +2,19 @@
|
|||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
import { Zap } from 'lucide-react';
|
|
||||||
import { ToggleLeft, ToggleRight } from 'lucide-react';
|
|
||||||
import { Loader } from 'lucide-react';
|
import { Loader } from 'lucide-react';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
|
import { toFriendlyWebhookEventName } from '@documenso/lib/universal/webhook/to-friendly-webhook-event-name';
|
||||||
import { trpc } from '@documenso/trpc/react';
|
import { trpc } from '@documenso/trpc/react';
|
||||||
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
|
import { Badge } from '@documenso/ui/primitives/badge';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
|
|
||||||
import { SettingsHeader } from '~/components/(dashboard)/settings/layout/header';
|
import { SettingsHeader } from '~/components/(dashboard)/settings/layout/header';
|
||||||
import { CreateWebhookDialog } from '~/components/(dashboard)/settings/webhooks/create-webhook-dialog';
|
import { CreateWebhookDialog } from '~/components/(dashboard)/settings/webhooks/create-webhook-dialog';
|
||||||
import { DeleteWebhookDialog } from '~/components/(dashboard)/settings/webhooks/delete-webhook-dialog';
|
import { DeleteWebhookDialog } from '~/components/(dashboard)/settings/webhooks/delete-webhook-dialog';
|
||||||
|
import { LocaleDate } from '~/components/formatter/locale-date';
|
||||||
|
|
||||||
export default function WebhookPage() {
|
export default function WebhookPage() {
|
||||||
const { data: webhooks, isLoading } = trpc.webhook.getWebhooks.useQuery();
|
const { data: webhooks, isLoading } = trpc.webhook.getWebhooks.useQuery();
|
||||||
@@ -43,35 +46,46 @@ export default function WebhookPage() {
|
|||||||
{webhooks && webhooks.length > 0 && (
|
{webhooks && webhooks.length > 0 && (
|
||||||
<div className="mt-4 flex max-w-xl flex-col gap-y-4">
|
<div className="mt-4 flex max-w-xl flex-col gap-y-4">
|
||||||
{webhooks?.map((webhook) => (
|
{webhooks?.map((webhook) => (
|
||||||
<div key={webhook.id} className="border-border rounded-lg border p-4">
|
<div
|
||||||
|
key={webhook.id}
|
||||||
|
className={cn(
|
||||||
|
'border-border rounded-lg border p-4',
|
||||||
|
!webhook.enabled && 'bg-muted/40',
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div className="flex items-center justify-between gap-x-4">
|
<div className="flex items-center justify-between gap-x-4">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-lg font-semibold">Webhook URL</h4>
|
<div className="truncate font-mono text-xs">{webhook.id}</div>
|
||||||
<p className="text-muted-foreground break-all">{webhook.webhookUrl}</p>
|
|
||||||
<h4 className="mt-4 text-lg font-semibold">Event triggers</h4>
|
<div className="mt-1.5 flex items-center gap-2">
|
||||||
{webhook.eventTriggers.map((trigger, index) => (
|
<h5 className="text-sm">{webhook.webhookUrl}</h5>
|
||||||
<span key={index} className="text-muted-foreground flex flex-row items-center">
|
|
||||||
<Zap className="mr-1 h-4 w-4" /> {trigger}
|
<Badge variant={webhook.enabled ? 'neutral' : 'warning'} size="small">
|
||||||
</span>
|
{webhook.enabled ? 'Enabled' : 'Disabled'}
|
||||||
))}
|
</Badge>
|
||||||
{webhook.enabled ? (
|
</div>
|
||||||
<h4 className="mt-4 flex items-center gap-2 text-lg">
|
|
||||||
Active <ToggleRight className="h-6 w-6 fill-green-200 stroke-green-400" />
|
<p className="text-muted-foreground mt-2 text-xs">
|
||||||
</h4>
|
Listening to{' '}
|
||||||
) : (
|
{webhook.eventTriggers
|
||||||
<h4 className="mt-4 flex items-center gap-2 text-lg">
|
.map((trigger) => toFriendlyWebhookEventName(trigger))
|
||||||
Inactive <ToggleLeft className="h-6 w-6 fill-slate-200 stroke-slate-400" />
|
.join(', ')}
|
||||||
</h4>
|
</p>
|
||||||
)}
|
|
||||||
|
<p className="text-muted-foreground mt-2 text-xs">
|
||||||
|
Created on{' '}
|
||||||
|
<LocaleDate date={webhook.createdAt} format={DateTime.DATETIME_FULL} />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-shrink-0 gap-4">
|
||||||
|
<Button asChild variant="outline">
|
||||||
|
<Link href={`/settings/webhooks/${webhook.id}`}>Edit</Link>
|
||||||
|
</Button>
|
||||||
|
<DeleteWebhookDialog webhook={webhook}>
|
||||||
|
<Button variant="destructive">Delete</Button>
|
||||||
|
</DeleteWebhookDialog>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div className="mt-6 flex flex-col-reverse space-y-2 space-y-reverse sm:mt-0 sm:flex-row sm:justify-end sm:space-x-2 sm:space-y-0">
|
|
||||||
<Button asChild variant="outline">
|
|
||||||
<Link href={`/settings/webhooks/${webhook.id}`}>Edit</Link>
|
|
||||||
</Button>
|
|
||||||
<DeleteWebhookDialog webhook={webhook}>
|
|
||||||
<Button variant="destructive">Delete</Button>
|
|
||||||
</DeleteWebhookDialog>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -51,19 +51,6 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Link href="/settings/webhooks">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className={cn(
|
|
||||||
'w-full justify-start',
|
|
||||||
pathname?.startsWith('/settings/webhooks') && 'bg-secondary',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Webhook className="mr-2 h-5 w-5" />
|
|
||||||
Webhooks
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link href="/settings/security">
|
<Link href="/settings/security">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -90,6 +77,19 @@ export const DesktopNav = ({ className, ...props }: DesktopNavProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Link href="/settings/webhooks">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className={cn(
|
||||||
|
'w-full justify-start',
|
||||||
|
pathname?.startsWith('/settings/webhooks') && 'bg-secondary',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Webhook className="mr-2 h-5 w-5" />
|
||||||
|
Webhooks
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
{isBillingEnabled && (
|
{isBillingEnabled && (
|
||||||
<Link href="/settings/billing">
|
<Link href="/settings/billing">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -54,19 +54,6 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Link href="/settings/webhooks">
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className={cn(
|
|
||||||
'w-full justify-start',
|
|
||||||
pathname?.startsWith('/settings/webhooks') && 'bg-secondary',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Webhook className="mr-2 h-5 w-5" />
|
|
||||||
Webhooks
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<Link href="/settings/security">
|
<Link href="/settings/security">
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -93,6 +80,19 @@ export const MobileNav = ({ className, ...props }: MobileNavProps) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
<Link href="/settings/webhooks">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className={cn(
|
||||||
|
'w-full justify-start',
|
||||||
|
pathname?.startsWith('/settings/webhooks') && 'bg-secondary',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Webhook className="mr-2 h-5 w-5" />
|
||||||
|
Webhooks
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
|
||||||
{isBillingEnabled && (
|
{isBillingEnabled && (
|
||||||
<Link href="/settings/billing">
|
<Link href="/settings/billing">
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
|
FormDescription,
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
@@ -34,7 +35,7 @@ import { PasswordInput } from '@documenso/ui/primitives/password-input';
|
|||||||
import { Switch } from '@documenso/ui/primitives/switch';
|
import { Switch } from '@documenso/ui/primitives/switch';
|
||||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||||
|
|
||||||
import { MultiSelectCombobox } from './multiselect-combobox';
|
import { TriggerMultiSelectCombobox } from './trigger-multiselect-combobox';
|
||||||
|
|
||||||
type TCreateWebhookFormSchema = z.infer<typeof ZCreateWebhookFormSchema>;
|
type TCreateWebhookFormSchema = z.infer<typeof ZCreateWebhookFormSchema>;
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
|||||||
{trigger ?? <Button className="flex-shrink-0">Create Webhook</Button>}
|
{trigger ?? <Button className="flex-shrink-0">Create Webhook</Button>}
|
||||||
</DialogTrigger>
|
</DialogTrigger>
|
||||||
|
|
||||||
<DialogContent position="center">
|
<DialogContent className="max-w-lg" position="center">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Create webhook</DialogTitle>
|
<DialogTitle>Create webhook</DialogTitle>
|
||||||
<DialogDescription>On this page, you can create a new webhook.</DialogDescription>
|
<DialogDescription>On this page, you can create a new webhook.</DialogDescription>
|
||||||
@@ -104,34 +105,68 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
|||||||
className="flex h-full flex-col space-y-4"
|
className="flex h-full flex-col space-y-4"
|
||||||
disabled={form.formState.isSubmitting}
|
disabled={form.formState.isSubmitting}
|
||||||
>
|
>
|
||||||
<FormField
|
<div className="flex flex-col-reverse gap-4 md:flex-row">
|
||||||
control={form.control}
|
<FormField
|
||||||
name="webhookUrl"
|
control={form.control}
|
||||||
render={({ field }) => (
|
name="webhookUrl"
|
||||||
<FormItem>
|
render={({ field }) => (
|
||||||
<FormLabel required>Webhook URL</FormLabel>
|
<FormItem className="flex-1">
|
||||||
<FormControl>
|
<FormLabel required>Webhook URL</FormLabel>
|
||||||
<Input className="bg-background" {...field} />
|
<FormControl>
|
||||||
</FormControl>
|
<Input className="bg-background" {...field} />
|
||||||
<FormMessage />
|
</FormControl>
|
||||||
</FormItem>
|
|
||||||
)}
|
<FormDescription>
|
||||||
/>
|
The URL for Documenso to send webhook events to.
|
||||||
|
</FormDescription>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="enabled"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Enabled</FormLabel>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
className="bg-background"
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="eventTriggers"
|
name="eventTriggers"
|
||||||
render={({ field: { onChange, value } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<FormItem className="flex flex-col gap-2">
|
<FormItem className="flex flex-col gap-2">
|
||||||
<FormLabel required>Event triggers</FormLabel>
|
<FormLabel required>Triggers</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<MultiSelectCombobox
|
<TriggerMultiSelectCombobox
|
||||||
listValues={value}
|
listValues={value}
|
||||||
onChange={(values: string[]) => {
|
onChange={(values: string[]) => {
|
||||||
onChange(values);
|
onChange(values);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
|
<FormDescription>
|
||||||
|
The events that will trigger a webhook to be sent to your URL.
|
||||||
|
</FormDescription>
|
||||||
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@@ -150,24 +185,11 @@ export const CreateWebhookDialog = ({ trigger, ...props }: CreateWebhookDialogPr
|
|||||||
value={field.value ?? ''}
|
value={field.value ?? ''}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
|
||||||
</FormItem>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<FormField
|
<FormDescription>
|
||||||
control={form.control}
|
A secret that will be sent to your URL so you can verify that the request has
|
||||||
name="enabled"
|
been sent by Documenso.
|
||||||
render={({ field }) => (
|
</FormDescription>
|
||||||
<FormItem className="flex items-center gap-2">
|
|
||||||
<FormLabel className="mt-2">Active</FormLabel>
|
|
||||||
<FormControl>
|
|
||||||
<Switch
|
|
||||||
className="bg-background"
|
|
||||||
checked={field.value}
|
|
||||||
onCheckedChange={field.onChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import * as React from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { WebhookTriggerEvents } from '@prisma/client/';
|
import { WebhookTriggerEvents } from '@prisma/client/';
|
||||||
import { Check, ChevronsUpDown } from 'lucide-react';
|
import { Check, ChevronsUpDown } from 'lucide-react';
|
||||||
|
|
||||||
|
import { toFriendlyWebhookEventName } from '@documenso/lib/universal/webhook/to-friendly-webhook-event-name';
|
||||||
import { cn } from '@documenso/ui/lib/utils';
|
import { cn } from '@documenso/ui/lib/utils';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import {
|
import {
|
||||||
@@ -16,18 +17,21 @@ import { Popover, PopoverContent, PopoverTrigger } from '@documenso/ui/primitive
|
|||||||
|
|
||||||
import { truncateTitle } from '~/helpers/truncate-title';
|
import { truncateTitle } from '~/helpers/truncate-title';
|
||||||
|
|
||||||
type ComboboxProps = {
|
type TriggerMultiSelectComboboxProps = {
|
||||||
listValues: string[];
|
listValues: string[];
|
||||||
onChange: (_values: string[]) => void;
|
onChange: (_values: string[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MultiSelectCombobox = ({ listValues, onChange }: ComboboxProps) => {
|
export const TriggerMultiSelectCombobox = ({
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
listValues,
|
||||||
const [selectedValues, setSelectedValues] = React.useState<string[]>([]);
|
onChange,
|
||||||
|
}: TriggerMultiSelectComboboxProps) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [selectedValues, setSelectedValues] = useState<string[]>([]);
|
||||||
|
|
||||||
const triggerEvents = Object.values(WebhookTriggerEvents);
|
const triggerEvents = Object.values(WebhookTriggerEvents);
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedValues(listValues);
|
setSelectedValues(listValues);
|
||||||
}, [listValues]);
|
}, [listValues]);
|
||||||
|
|
||||||
@@ -35,6 +39,7 @@ const MultiSelectCombobox = ({ listValues, onChange }: ComboboxProps) => {
|
|||||||
|
|
||||||
const handleSelect = (currentValue: string) => {
|
const handleSelect = (currentValue: string) => {
|
||||||
let newSelectedValues;
|
let newSelectedValues;
|
||||||
|
|
||||||
if (selectedValues.includes(currentValue)) {
|
if (selectedValues.includes(currentValue)) {
|
||||||
newSelectedValues = selectedValues.filter((value) => value !== currentValue);
|
newSelectedValues = selectedValues.filter((value) => value !== currentValue);
|
||||||
} else {
|
} else {
|
||||||
@@ -59,9 +64,14 @@ const MultiSelectCombobox = ({ listValues, onChange }: ComboboxProps) => {
|
|||||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContent className="z-9999 w-[200px] p-0">
|
<PopoverContent className="z-9999 w-full max-w-[280px] p-0">
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder={truncateTitle(selectedValues.join(', '), 15)} />
|
<CommandInput
|
||||||
|
placeholder={truncateTitle(
|
||||||
|
selectedValues.map((v) => toFriendlyWebhookEventName(v)).join(', '),
|
||||||
|
15,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<CommandEmpty>No value found.</CommandEmpty>
|
<CommandEmpty>No value found.</CommandEmpty>
|
||||||
<CommandGroup>
|
<CommandGroup>
|
||||||
{allEvents.map((value: string, i: number) => (
|
{allEvents.map((value: string, i: number) => (
|
||||||
@@ -72,7 +82,7 @@ const MultiSelectCombobox = ({ listValues, onChange }: ComboboxProps) => {
|
|||||||
selectedValues.includes(value) ? 'opacity-100' : 'opacity-0',
|
selectedValues.includes(value) ? 'opacity-100' : 'opacity-0',
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{value}
|
{toFriendlyWebhookEventName(value)}
|
||||||
</CommandItem>
|
</CommandItem>
|
||||||
))}
|
))}
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
@@ -81,5 +91,3 @@ const MultiSelectCombobox = ({ listValues, onChange }: ComboboxProps) => {
|
|||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { MultiSelectCombobox };
|
|
||||||
@@ -5,6 +5,7 @@ import { validateApiToken } from '@documenso/lib/server-only/webhooks/zapier/val
|
|||||||
export const testCredentialsHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
export const testCredentialsHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
try {
|
try {
|
||||||
const { authorization } = req.headers;
|
const { authorization } = req.headers;
|
||||||
|
|
||||||
const user = await validateApiToken({ authorization });
|
const user = await validateApiToken({ authorization });
|
||||||
|
|
||||||
return res.status(200).json({
|
return res.status(200).json({
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { prisma } from '@documenso/prisma';
|
|
||||||
|
|
||||||
export interface GetUserWebhooksByIdOptions {
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getUserWebhooksById = async ({ id }: GetUserWebhooksByIdOptions) => {
|
|
||||||
return await prisma.user.findFirstOrThrow({
|
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
select: {
|
|
||||||
email: true,
|
|
||||||
Webhooks: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const toFriendlyWebhookEventName = (eventName: string) => {
|
||||||
|
return eventName.replace(/_/g, '.').toLowerCase();
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user