betterstart-cli 0.0.28 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/adapters/next/integrations/mailchimp/actions/mailchimp.ts +0 -1
- package/dist/assets/adapters/next/plugins/blog/schemas/menus.json +1 -1
- package/dist/assets/adapters/next/plugins/blog/schemas/posts.json +1 -1
- package/dist/assets/adapters/next/templates/init/admin-globals.css +1 -1
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-nav-link.tsx +12 -2
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-settings-sidebar.tsx +75 -0
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar-nav-link.tsx +12 -2
- package/dist/assets/adapters/next/templates/init/components/layouts/admin-sidebar.tsx +2 -8
- package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-grid.tsx +14 -5
- package/dist/assets/adapters/next/templates/init/components/shared/data-table/data-table.tsx +111 -112
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/dev-mode-types.ts +4 -0
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode-integrate.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/entity-versions/entity-versions-drawer.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-url-importer.tsx +45 -47
- package/dist/assets/adapters/next/templates/init/components/shared/page-header.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/data/webhook-events.ts +5 -0
- package/dist/assets/adapters/next/templates/init/hooks/use-table-utils.ts +27 -7
- package/dist/assets/adapters/next/templates/init/hooks/use-webhooks.ts +211 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/audit/types.ts +2 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/index.ts +0 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/types.ts +0 -4
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/upsert-form-settings.ts +0 -2
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/create-webhook.ts +55 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/delete-webhook.ts +26 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/dispatch.ts +135 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/get-webhook-deliveries.ts +124 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/get-webhook-secret.ts +28 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/get-webhook-subscriptions.ts +32 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/get-webhooks.ts +154 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/index.ts +30 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/internal-get-active-endpoints.ts +20 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/regenerate-webhook-secret.ts +35 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/register.ts +70 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/send-test-webhook.ts +81 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/types.ts +104 -0
- package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/update-webhook.ts +72 -0
- package/dist/assets/adapters/next/templates/init/lib/db/core/schema.ts +71 -2
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/register.ts +2 -0
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/runner.ts +3 -1
- package/dist/assets/adapters/next/templates/init/lib/lifecycle-hooks/types.ts +6 -1
- package/dist/assets/adapters/next/templates/init/pages/auth-gate-rsc.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/media/media-page-content.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/media/media-page-skeleton.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-columns.tsx +150 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-page-content.tsx +137 -0
- package/dist/assets/adapters/next/templates/init/pages/{audit-log → settings/audit-log}/audit-log-page-skeleton.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/settings/audit-log/audit-log-table.tsx +295 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/edit-form-notifications-dialog.tsx +128 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-page-content.tsx +111 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-page-skeleton.tsx +7 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/forms/forms-settings-page.tsx +14 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/settings-layout.tsx +10 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/delete-webhook-dialog.tsx +82 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhook-actions.tsx +53 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhook-enabled-switch.tsx +18 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhook-endpoint-dialog.tsx +370 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-columns.tsx +258 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-logs-columns.tsx +200 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-logs-page-content.tsx +117 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-logs-page.tsx +14 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-logs-table.tsx +253 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-page-content.tsx +161 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-page-skeleton.tsx +7 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-page.tsx +14 -0
- package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhooks-table.tsx +264 -0
- package/dist/assets/adapters/next/templates/init/pages/users/users-page-skeleton.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/pages/users/users-table.tsx +103 -109
- package/dist/assets/adapters/next/templates/init/types/webhooks.ts +7 -0
- package/dist/assets/adapters/next/templates/init/utils/webhook/signature.ts +13 -0
- package/dist/assets/shared-assets/react-admin/ui/card.tsx +1 -1
- package/dist/assets/shared-assets/react-admin/ui/drawer.tsx +2 -2
- package/dist/assets/shared-assets/react-admin/ui/input-group.tsx +3 -3
- package/dist/assets/shared-assets/react-admin/ui/sidebar.tsx +1 -1
- package/dist/cli.js +947 -620
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/adapters/next/templates/init/lib/actions/forms/test-form-webhook.ts +0 -40
- package/dist/assets/adapters/next/templates/init/pages/audit-log/audit-log-page-content.tsx +0 -550
- package/dist/assets/adapters/next/templates/init/utils/webhook/webhook.ts +0 -28
- /package/dist/assets/adapters/next/templates/init/pages/{audit-log → settings/audit-log}/audit-log-detail-drawer.tsx +0 -0
- /package/dist/assets/adapters/next/templates/init/pages/{audit-log → settings/audit-log}/audit-log-detail-row.tsx +0 -0
- /package/dist/assets/adapters/next/templates/init/pages/{audit-log → settings/audit-log}/audit-log-page.tsx +0 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { upsertFormSettings } from '@admin/actions/forms'
|
|
4
|
+
import { Button } from '@admin/components/ui/button'
|
|
5
|
+
import {
|
|
6
|
+
Dialog,
|
|
7
|
+
DialogBody,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogFooter,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle
|
|
13
|
+
} from '@admin/components/ui/dialog'
|
|
14
|
+
import {
|
|
15
|
+
Form,
|
|
16
|
+
FormControl,
|
|
17
|
+
FormDescription,
|
|
18
|
+
FormField,
|
|
19
|
+
FormItem,
|
|
20
|
+
FormLabel,
|
|
21
|
+
FormMessage
|
|
22
|
+
} from '@admin/components/ui/form'
|
|
23
|
+
import { Textarea } from '@admin/components/ui/textarea'
|
|
24
|
+
import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
|
|
25
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
26
|
+
import { LoaderCircle } from 'lucide-react'
|
|
27
|
+
import { useForm } from 'react-hook-form'
|
|
28
|
+
import { toast } from 'sonner'
|
|
29
|
+
import { z } from 'zod/v3'
|
|
30
|
+
|
|
31
|
+
const notificationsSchema = z.object({
|
|
32
|
+
notificationEmails: z.string().trim()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
type NotificationsFormValues = z.infer<typeof notificationsSchema>
|
|
36
|
+
|
|
37
|
+
interface EditFormNotificationsDialogProps {
|
|
38
|
+
formLabel: string
|
|
39
|
+
settingsKey: string
|
|
40
|
+
initialEmails: string
|
|
41
|
+
open: boolean
|
|
42
|
+
onOpenChange: (open: boolean) => void
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function EditFormNotificationsDialog({
|
|
46
|
+
formLabel,
|
|
47
|
+
settingsKey,
|
|
48
|
+
initialEmails,
|
|
49
|
+
open,
|
|
50
|
+
onOpenChange
|
|
51
|
+
}: EditFormNotificationsDialogProps) {
|
|
52
|
+
const queryClient = useQueryClient()
|
|
53
|
+
|
|
54
|
+
const form = useForm<NotificationsFormValues>({
|
|
55
|
+
resolver: standardSchemaResolver(notificationsSchema),
|
|
56
|
+
defaultValues: { notificationEmails: initialEmails }
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const mutation = useMutation({
|
|
60
|
+
mutationFn: async (values: NotificationsFormValues) => {
|
|
61
|
+
const result = await upsertFormSettings(settingsKey, {
|
|
62
|
+
notificationEmails: values.notificationEmails || null
|
|
63
|
+
})
|
|
64
|
+
if (!result.success) {
|
|
65
|
+
throw new Error(result.error || 'Failed to save notification emails')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return result
|
|
69
|
+
},
|
|
70
|
+
onSuccess: async () => {
|
|
71
|
+
toast.success('Notification emails saved')
|
|
72
|
+
await queryClient.invalidateQueries({ queryKey: ['form-settings'] })
|
|
73
|
+
onOpenChange(false)
|
|
74
|
+
},
|
|
75
|
+
onError: (error: Error) => {
|
|
76
|
+
toast.error(error.message || 'An unexpected error occurred')
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const isPending = mutation.isPending
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
84
|
+
<DialogContent className="sm:max-w-135">
|
|
85
|
+
<Form {...form}>
|
|
86
|
+
<form onSubmit={form.handleSubmit((values) => mutation.mutate(values))}>
|
|
87
|
+
<DialogHeader>
|
|
88
|
+
<DialogTitle>{formLabel} notifications</DialogTitle>
|
|
89
|
+
<DialogDescription>
|
|
90
|
+
Send an email to these addresses when a submission arrives
|
|
91
|
+
</DialogDescription>
|
|
92
|
+
</DialogHeader>
|
|
93
|
+
|
|
94
|
+
<DialogBody>
|
|
95
|
+
<FormField
|
|
96
|
+
control={form.control}
|
|
97
|
+
name="notificationEmails"
|
|
98
|
+
render={({ field }) => (
|
|
99
|
+
<FormItem>
|
|
100
|
+
<FormLabel>Notification emails</FormLabel>
|
|
101
|
+
<FormControl>
|
|
102
|
+
<Textarea
|
|
103
|
+
placeholder="sales@example.com, team@example.com"
|
|
104
|
+
disabled={isPending}
|
|
105
|
+
{...field}
|
|
106
|
+
/>
|
|
107
|
+
</FormControl>
|
|
108
|
+
<FormDescription>
|
|
109
|
+
Comma-separated email addresses. Leave empty to disable notifications.
|
|
110
|
+
</FormDescription>
|
|
111
|
+
<FormMessage />
|
|
112
|
+
</FormItem>
|
|
113
|
+
)}
|
|
114
|
+
/>
|
|
115
|
+
</DialogBody>
|
|
116
|
+
|
|
117
|
+
<DialogFooter showCloseButton={true}>
|
|
118
|
+
<Button type="submit" disabled={isPending || !form.formState.isDirty} size="lg">
|
|
119
|
+
{isPending && <LoaderCircle className="animate-spin" />}
|
|
120
|
+
Save
|
|
121
|
+
</Button>
|
|
122
|
+
</DialogFooter>
|
|
123
|
+
</form>
|
|
124
|
+
</Form>
|
|
125
|
+
</DialogContent>
|
|
126
|
+
</Dialog>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { PageHeader } from '@admin/components/shared/page-header'
|
|
4
|
+
import { Button } from '@admin/components/ui/button'
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle
|
|
11
|
+
} from '@admin/components/ui/card'
|
|
12
|
+
import {
|
|
13
|
+
Table,
|
|
14
|
+
TableBody,
|
|
15
|
+
TableCell,
|
|
16
|
+
TableHead,
|
|
17
|
+
TableHeader,
|
|
18
|
+
TableRow
|
|
19
|
+
} from '@admin/components/ui/table'
|
|
20
|
+
import { webhookEventSources } from '@admin/data/webhook-events'
|
|
21
|
+
import { useFormSettingsList } from '@admin/hooks/use-webhooks'
|
|
22
|
+
import * as React from 'react'
|
|
23
|
+
import { EditFormNotificationsDialog } from './edit-form-notifications-dialog'
|
|
24
|
+
|
|
25
|
+
export function FormsSettingsPageContent() {
|
|
26
|
+
const formSources = webhookEventSources.filter((source) => source.kind === 'form')
|
|
27
|
+
const { data: settings, isPending } = useFormSettingsList()
|
|
28
|
+
const [editingKey, setEditingKey] = React.useState<string | null>(null)
|
|
29
|
+
|
|
30
|
+
const editingSource = formSources.find((source) => source.settingsKey === editingKey)
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<React.Fragment>
|
|
34
|
+
<PageHeader title="Form Settings" />
|
|
35
|
+
<main className="mx-auto max-w-5xl w-full space-y-4 p-4">
|
|
36
|
+
<Card>
|
|
37
|
+
<CardHeader>
|
|
38
|
+
<CardTitle>Form notifications</CardTitle>
|
|
39
|
+
<CardDescription>
|
|
40
|
+
Emails notified when a submission arrives. Webhooks are managed on the Webhooks tab.
|
|
41
|
+
</CardDescription>
|
|
42
|
+
</CardHeader>
|
|
43
|
+
<CardContent>
|
|
44
|
+
{formSources.length === 0 ? (
|
|
45
|
+
<p className="text-sm text-muted-foreground">
|
|
46
|
+
No forms yet. Create a form schema to configure its notifications here.
|
|
47
|
+
</p>
|
|
48
|
+
) : (
|
|
49
|
+
<Table>
|
|
50
|
+
<TableHeader>
|
|
51
|
+
<TableRow>
|
|
52
|
+
<TableHead>Form</TableHead>
|
|
53
|
+
<TableHead>Notification emails</TableHead>
|
|
54
|
+
<TableHead className="w-20" />
|
|
55
|
+
</TableRow>
|
|
56
|
+
</TableHeader>
|
|
57
|
+
<TableBody>
|
|
58
|
+
{formSources.map((source) => {
|
|
59
|
+
const formSettings = settings?.find(
|
|
60
|
+
(entry) => entry.formName === source.settingsKey
|
|
61
|
+
)
|
|
62
|
+
return (
|
|
63
|
+
<TableRow key={source.schema}>
|
|
64
|
+
<TableCell className="font-medium">{source.label}</TableCell>
|
|
65
|
+
<TableCell>
|
|
66
|
+
{isPending ? (
|
|
67
|
+
<span className="text-muted-foreground">Loading…</span>
|
|
68
|
+
) : formSettings?.notificationEmails ? (
|
|
69
|
+
<span className="font-mono text-xs">
|
|
70
|
+
{formSettings.notificationEmails}
|
|
71
|
+
</span>
|
|
72
|
+
) : (
|
|
73
|
+
<span className="text-muted-foreground">No emails configured</span>
|
|
74
|
+
)}
|
|
75
|
+
</TableCell>
|
|
76
|
+
<TableCell className="text-right">
|
|
77
|
+
<Button
|
|
78
|
+
variant="outline"
|
|
79
|
+
size="sm"
|
|
80
|
+
onClick={() => setEditingKey(source.settingsKey ?? source.schema)}
|
|
81
|
+
>
|
|
82
|
+
Edit
|
|
83
|
+
</Button>
|
|
84
|
+
</TableCell>
|
|
85
|
+
</TableRow>
|
|
86
|
+
)
|
|
87
|
+
})}
|
|
88
|
+
</TableBody>
|
|
89
|
+
</Table>
|
|
90
|
+
)}
|
|
91
|
+
</CardContent>
|
|
92
|
+
</Card>
|
|
93
|
+
</main>
|
|
94
|
+
{editingSource?.settingsKey && (
|
|
95
|
+
<EditFormNotificationsDialog
|
|
96
|
+
key={editingSource.settingsKey}
|
|
97
|
+
formLabel={editingSource.label}
|
|
98
|
+
settingsKey={editingSource.settingsKey}
|
|
99
|
+
initialEmails={
|
|
100
|
+
settings?.find((entry) => entry.formName === editingSource.settingsKey)
|
|
101
|
+
?.notificationEmails ?? ''
|
|
102
|
+
}
|
|
103
|
+
open={editingKey !== null}
|
|
104
|
+
onOpenChange={(nextOpen) => {
|
|
105
|
+
if (!nextOpen) setEditingKey(null)
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
)}
|
|
109
|
+
</React.Fragment>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { requireRole, UserRole } from '@admin/auth/middleware'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { FormsSettingsPageContent } from './forms-settings-page-content'
|
|
4
|
+
import { FormsSettingsPageSkeleton } from './forms-settings-page-skeleton'
|
|
5
|
+
|
|
6
|
+
export default async function FormsSettingsPage() {
|
|
7
|
+
await requireRole([UserRole.ADMIN, UserRole.EDITOR])
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<React.Suspense fallback={<FormsSettingsPageSkeleton />}>
|
|
11
|
+
<FormsSettingsPageContent />
|
|
12
|
+
</React.Suspense>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AdminSettingsSidebar } from '@admin/components/layouts/admin-settings-sidebar'
|
|
2
|
+
|
|
3
|
+
export default function SettingsLayout({ children }: { children: React.ReactNode }) {
|
|
4
|
+
return (
|
|
5
|
+
<div className="fixed inset-y-0 right-0 left-(--admin-shell-content-left) flex min-w-0 transition-[left] duration-200 ease-linear">
|
|
6
|
+
<AdminSettingsSidebar />
|
|
7
|
+
<div className="min-w-0 flex-1 overflow-auto">{children}</div>
|
|
8
|
+
</div>
|
|
9
|
+
)
|
|
10
|
+
}
|
package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/delete-webhook-dialog.tsx
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { WebhookEndpointListItem } from '@admin/actions/webhooks'
|
|
4
|
+
import { deleteWebhook } from '@admin/actions/webhooks'
|
|
5
|
+
import { Button } from '@admin/components/ui/button'
|
|
6
|
+
import {
|
|
7
|
+
Dialog,
|
|
8
|
+
DialogBody,
|
|
9
|
+
DialogContent,
|
|
10
|
+
DialogDescription,
|
|
11
|
+
DialogFooter,
|
|
12
|
+
DialogHeader,
|
|
13
|
+
DialogTitle
|
|
14
|
+
} from '@admin/components/ui/dialog'
|
|
15
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
16
|
+
import { LoaderCircle } from 'lucide-react'
|
|
17
|
+
import { toast } from 'sonner'
|
|
18
|
+
|
|
19
|
+
interface DeleteWebhookDialogProps {
|
|
20
|
+
webhook: WebhookEndpointListItem | null
|
|
21
|
+
open: boolean
|
|
22
|
+
onOpenChange: (open: boolean) => void
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function DeleteWebhookDialog({ webhook, open, onOpenChange }: DeleteWebhookDialogProps) {
|
|
26
|
+
const queryClient = useQueryClient()
|
|
27
|
+
|
|
28
|
+
const mutation = useMutation({
|
|
29
|
+
mutationFn: async () => {
|
|
30
|
+
if (!webhook) throw new Error('No endpoint selected')
|
|
31
|
+
const result = await deleteWebhook(webhook.id)
|
|
32
|
+
if (!result.success) {
|
|
33
|
+
throw new Error(result.error || 'Failed to delete endpoint')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result
|
|
37
|
+
},
|
|
38
|
+
onSuccess: async () => {
|
|
39
|
+
toast.success('Endpoint deleted')
|
|
40
|
+
await queryClient.invalidateQueries({ queryKey: ['webhooks'] })
|
|
41
|
+
await queryClient.invalidateQueries({
|
|
42
|
+
queryKey: ['webhook-subscriptions']
|
|
43
|
+
})
|
|
44
|
+
await queryClient.invalidateQueries({ queryKey: ['webhook-deliveries'] })
|
|
45
|
+
onOpenChange(false)
|
|
46
|
+
},
|
|
47
|
+
onError: (error: Error) => {
|
|
48
|
+
toast.error(error.message || 'An unexpected error occurred')
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const isPending = mutation.isPending
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
56
|
+
<DialogContent className="sm:max-w-105">
|
|
57
|
+
<DialogHeader>
|
|
58
|
+
<DialogTitle>Delete endpoint</DialogTitle>
|
|
59
|
+
<DialogDescription>
|
|
60
|
+
{webhook
|
|
61
|
+
? `"${webhook.name}" will stop receiving events. Its subscriptions and delivery history are removed too.`
|
|
62
|
+
: 'This endpoint will stop receiving events.'}
|
|
63
|
+
</DialogDescription>
|
|
64
|
+
</DialogHeader>
|
|
65
|
+
<DialogBody>
|
|
66
|
+
<p className="text-sm text-muted-foreground">This action cannot be undone.</p>
|
|
67
|
+
</DialogBody>
|
|
68
|
+
<DialogFooter showCloseButton={true}>
|
|
69
|
+
<Button
|
|
70
|
+
variant="destructive"
|
|
71
|
+
size="lg"
|
|
72
|
+
disabled={isPending}
|
|
73
|
+
onClick={() => mutation.mutate()}
|
|
74
|
+
>
|
|
75
|
+
{isPending && <LoaderCircle className="animate-spin" />}
|
|
76
|
+
Delete
|
|
77
|
+
</Button>
|
|
78
|
+
</DialogFooter>
|
|
79
|
+
</DialogContent>
|
|
80
|
+
</Dialog>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { WebhookEndpointListItem } from '@admin/actions/webhooks'
|
|
4
|
+
import { Button } from '@admin/components/ui/button'
|
|
5
|
+
import {
|
|
6
|
+
DropdownMenu,
|
|
7
|
+
DropdownMenuContent,
|
|
8
|
+
DropdownMenuItem,
|
|
9
|
+
DropdownMenuTrigger
|
|
10
|
+
} from '@admin/components/ui/dropdown-menu'
|
|
11
|
+
import { useSendTestWebhook } from '@admin/hooks/use-webhooks'
|
|
12
|
+
import { LoaderCircle, MoreHorizontal } from 'lucide-react'
|
|
13
|
+
|
|
14
|
+
interface WebhookActionsProps {
|
|
15
|
+
webhook: WebhookEndpointListItem
|
|
16
|
+
onEdit: (webhook: WebhookEndpointListItem) => void
|
|
17
|
+
onDelete: (webhook: WebhookEndpointListItem) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function WebhookActions({ webhook, onEdit, onDelete }: WebhookActionsProps) {
|
|
21
|
+
const sendTest = useSendTestWebhook()
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<DropdownMenu>
|
|
25
|
+
<DropdownMenuTrigger asChild>
|
|
26
|
+
<Button
|
|
27
|
+
variant="ghost"
|
|
28
|
+
className="size-8 p-0"
|
|
29
|
+
aria-label={`Open webhook actions for ${webhook.name}`}
|
|
30
|
+
>
|
|
31
|
+
<span className="sr-only">Open webhook actions</span>
|
|
32
|
+
{sendTest.isPending ? (
|
|
33
|
+
<LoaderCircle className="size-4 animate-spin" />
|
|
34
|
+
) : (
|
|
35
|
+
<MoreHorizontal className="size-4" />
|
|
36
|
+
)}
|
|
37
|
+
</Button>
|
|
38
|
+
</DropdownMenuTrigger>
|
|
39
|
+
<DropdownMenuContent align="end">
|
|
40
|
+
<DropdownMenuItem
|
|
41
|
+
disabled={sendTest.isPending}
|
|
42
|
+
onSelect={() => sendTest.mutate(webhook.id)}
|
|
43
|
+
>
|
|
44
|
+
Test
|
|
45
|
+
</DropdownMenuItem>
|
|
46
|
+
<DropdownMenuItem onSelect={() => onEdit(webhook)}>Edit</DropdownMenuItem>
|
|
47
|
+
<DropdownMenuItem variant="destructive" onSelect={() => onDelete(webhook)}>
|
|
48
|
+
Delete
|
|
49
|
+
</DropdownMenuItem>
|
|
50
|
+
</DropdownMenuContent>
|
|
51
|
+
</DropdownMenu>
|
|
52
|
+
)
|
|
53
|
+
}
|
package/dist/assets/adapters/next/templates/init/pages/settings/webhooks/webhook-enabled-switch.tsx
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import type { WebhookEndpointListItem } from '@admin/actions/webhooks'
|
|
4
|
+
import { Switch } from '@admin/components/ui/switch'
|
|
5
|
+
import { useToggleWebhookEnabled } from '@admin/hooks/use-webhooks'
|
|
6
|
+
|
|
7
|
+
export function WebhookEnabledSwitch({ webhook }: { webhook: WebhookEndpointListItem }) {
|
|
8
|
+
const toggleEnabled = useToggleWebhookEnabled()
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<Switch
|
|
12
|
+
checked={webhook.enabled}
|
|
13
|
+
disabled={toggleEnabled.isPending}
|
|
14
|
+
onCheckedChange={(checked) => toggleEnabled.mutate({ id: webhook.id, enabled: checked })}
|
|
15
|
+
aria-label={`Toggle ${webhook.name}`}
|
|
16
|
+
/>
|
|
17
|
+
)
|
|
18
|
+
}
|