@kyro-cms/admin 0.3.2 → 0.3.5
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/EditorClient-XEUOVAAC.js +466 -0
- package/dist/EditorClient-XEUOVAAC.js.map +1 -0
- package/dist/EditorClient-YLCGVDXY.cjs +468 -0
- package/dist/EditorClient-YLCGVDXY.cjs.map +1 -0
- package/dist/chunk-7KPIUCGT.js +384 -0
- package/dist/chunk-7KPIUCGT.js.map +1 -0
- package/dist/chunk-GOACG6R7.cjs +473 -0
- package/dist/chunk-GOACG6R7.cjs.map +1 -0
- package/dist/index.cjs +14861 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +1661 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +563 -0
- package/dist/index.js +14784 -0
- package/dist/index.js.map +1 -0
- package/package.json +19 -19
- package/src/components/ActionBar.tsx +7 -43
- package/src/components/Admin.tsx +138 -277
- package/src/components/ApiKeysManager.tsx +428 -419
- package/src/components/AuditLogsPage.tsx +35 -39
- package/src/components/AuthBridge.tsx +51 -0
- package/src/components/AutoForm.tsx +495 -1230
- package/src/components/BrandingHub.tsx +18 -19
- package/src/components/BulkActionsBar.tsx +1 -1
- package/src/components/CreateView.tsx +22 -36
- package/src/components/Dashboard.tsx +60 -84
- package/src/components/DetailView.tsx +113 -91
- package/src/components/DeveloperCenter.tsx +200 -198
- package/src/components/FieldRenderer.tsx +206 -0
- package/src/components/GraphQLPlayground.tsx +340 -480
- package/src/components/ListView.tsx +828 -254
- package/src/components/LoginPage.tsx +3 -4
- package/src/components/MarketplaceManager.tsx +254 -0
- package/src/components/MediaGallery.tsx +856 -1192
- package/src/components/PluginsManager.tsx +277 -0
- package/src/components/RestPlayground.tsx +398 -560
- package/src/components/SessionsManager.tsx +211 -0
- package/src/components/Sidebar.astro +179 -151
- package/src/components/ThemeProvider.tsx +7 -161
- package/src/components/UserManagement.tsx +162 -146
- package/src/components/UserMenu.tsx +110 -0
- package/src/components/WebhookManager.tsx +305 -367
- package/src/components/blocks/AccordionBlock.tsx +4 -4
- package/src/components/blocks/ArrayBlock.tsx +3 -3
- package/src/components/blocks/BlockEditModal.tsx +8 -8
- package/src/components/blocks/BlockWrapper.tsx +61 -0
- package/src/components/blocks/ButtonBlock.tsx +4 -4
- package/src/components/blocks/ChildBlocksTree.tsx +23 -25
- package/src/components/blocks/CodeBlock.tsx +15 -15
- package/src/components/blocks/ColumnsBlock.tsx +6 -44
- package/src/components/blocks/DividerBlock.tsx +3 -3
- package/src/components/blocks/FileBlock.tsx +4 -4
- package/src/components/blocks/HeadingBlock.tsx +6 -38
- package/src/components/blocks/HeroBlock.tsx +4 -4
- package/src/components/blocks/ImageBlock.tsx +4 -4
- package/src/components/blocks/LinkBlock.tsx +4 -4
- package/src/components/blocks/ListBlock.tsx +3 -3
- package/src/components/blocks/ParagraphBlock.tsx +12 -42
- package/src/components/blocks/RelationshipBlock.tsx +4 -4
- package/src/components/blocks/RichTextBlock.tsx +4 -4
- package/src/components/blocks/VStackBlock.tsx +5 -37
- package/src/components/blocks/VideoBlock.tsx +4 -4
- package/src/components/blocks/types.ts +11 -0
- package/src/components/fields/AccordionField.tsx +1 -1
- package/src/components/fields/ArrayField.tsx +2 -2
- package/src/components/fields/ArrayLayout.tsx +93 -0
- package/src/components/fields/BlocksField.tsx +122 -111
- package/src/components/fields/ButtonField.tsx +1 -1
- package/src/components/fields/CheckboxField.tsx +14 -15
- package/src/components/fields/ChildrenField.tsx +2 -2
- package/src/components/fields/CodeField.tsx +3 -3
- package/src/components/fields/ColumnsField.tsx +2 -2
- package/src/components/fields/DateField.tsx +13 -26
- package/src/components/fields/EditorClient.tsx +26 -28
- package/src/components/fields/FieldLayout.tsx +52 -0
- package/src/components/fields/GroupLayout.tsx +35 -0
- package/src/components/fields/JSONField.tsx +7 -7
- package/src/components/fields/LinkField.tsx +1 -1
- package/src/components/fields/MarkdownField.tsx +1 -1
- package/src/components/fields/NumberField.tsx +13 -26
- package/src/components/fields/PortableTextField.tsx +4 -4
- package/src/components/fields/PortableTextRenderer.tsx +1 -1
- package/src/components/fields/RelationshipBlockField.tsx +31 -23
- package/src/components/fields/RelationshipField.tsx +14 -14
- package/src/components/fields/SelectField.tsx +17 -26
- package/src/components/fields/TabsLayout.tsx +69 -0
- package/src/components/fields/TextField.tsx +85 -38
- package/src/components/fields/UploadField.tsx +71 -41
- package/src/components/fields/VideoField.tsx +1 -1
- package/src/components/fields/extensions/blockComponents.tsx +2 -2
- package/src/components/fields/extensions/blocksStore.ts +207 -193
- package/src/components/fields/types.ts +22 -0
- package/src/components/layout/Layout.tsx +1 -1
- package/src/components/ui/ActionMenu.tsx +63 -0
- package/src/components/ui/Badge.tsx +59 -5
- package/src/components/ui/BlockDrawer.tsx +4 -5
- package/src/components/ui/CommandPalette.tsx +58 -36
- package/src/components/ui/CommandPaletteWrapper.tsx +18 -17
- package/src/components/ui/Dropdown.tsx +18 -16
- package/src/components/ui/EmptyState.tsx +25 -0
- package/src/components/ui/GlobalModal.tsx +49 -0
- package/src/components/ui/IconButton.tsx +44 -0
- package/src/components/ui/Modal.tsx +19 -20
- package/src/components/ui/PageHeader.tsx +158 -0
- package/src/components/ui/Pagination.tsx +61 -0
- package/src/components/ui/PromptModal.tsx +1 -1
- package/src/components/ui/SearchInput.tsx +57 -0
- package/src/components/ui/SeoPreview.tsx +31 -0
- package/src/components/ui/SessionModal.tsx +0 -0
- package/src/components/ui/SlidePanel.tsx +2 -0
- package/src/components/ui/Toast.tsx +65 -122
- package/src/components/ui/Toaster.tsx +18 -0
- package/src/components/ui/icons.tsx +112 -0
- package/src/components/users/UserDetail.tsx +290 -0
- package/src/components/users/UserForm.tsx +242 -0
- package/src/components/users/UsersList.tsx +338 -0
- package/src/env.d.ts +13 -13
- package/src/fields/index.ts +2 -1
- package/src/global.d.ts +7 -0
- package/src/hooks/data.ts +2 -9
- package/src/hooks/useAsyncData.ts +36 -0
- package/src/hooks/useAutoFormState.ts +527 -0
- package/src/hooks/useSelection.ts +49 -0
- package/src/hooks/useSession.ts +0 -0
- package/src/index.ts +11 -1
- package/src/integration.ts +86 -11
- package/src/kyro-cms.d.ts +209 -0
- package/src/layouts/AdminLayout.astro +128 -11
- package/src/layouts/AuthLayout.astro +21 -5
- package/src/lib/api.ts +175 -55
- package/src/lib/autoform-store.ts +435 -0
- package/src/lib/config.ts +82 -34
- package/src/lib/createRegistry.ts +29 -0
- package/src/lib/default-kyro-config.ts +4 -0
- package/src/lib/globals.ts +50 -0
- package/src/lib/media-utils.ts +18 -0
- package/src/lib/object-utils.ts +77 -0
- package/src/lib/paths.ts +61 -0
- package/src/lib/stores/index.ts +370 -0
- package/src/lib/types.ts +43 -0
- package/src/lib/useResourceManager.ts +105 -0
- package/src/pages/403.astro +67 -0
- package/src/pages/[collection]/[id].astro +14 -180
- package/src/pages/[collection]/index.astro +11 -6
- package/src/pages/api-explorer.astro +173 -0
- package/src/pages/audit/index.astro +2 -0
- package/src/pages/auth/login.astro +122 -0
- package/src/pages/auth/register.astro +167 -0
- package/src/pages/graphql-explorer.astro +59 -0
- package/src/pages/{admin/graphql.astro → graphql.astro} +51 -17
- package/src/pages/index.astro +577 -0
- package/src/pages/index_ALT.astro +3 -0
- package/src/pages/keys.astro +11 -0
- package/src/pages/marketplace.astro +11 -0
- package/src/pages/media.astro +3 -0
- package/src/pages/plugins.astro +8 -0
- package/src/pages/preview/[collection]/[id].astro +188 -123
- package/src/pages/rest-playground.astro +62 -0
- package/src/pages/roles/index.astro +183 -76
- package/src/pages/sessions.astro +8 -0
- package/src/pages/settings/[slug].astro +92 -114
- package/src/pages/settings/index.astro +5 -3
- package/src/pages/users/[id].astro +25 -154
- package/src/pages/users/index.astro +19 -130
- package/src/pages/users/new.astro +9 -86
- package/src/pages/webhooks.astro +11 -0
- package/src/routes.ts +80 -0
- package/src/styles/main.css +119 -79
- package/src/theme/tokens.ts +1 -0
- package/src/vite-env.d.ts +14 -0
- package/src/collections/auth/index.ts +0 -155
- package/src/collections/portfolio/index.ts +0 -343
- package/src/components/ApiExplorer.tsx +0 -325
- package/src/components/EnhancedListView.tsx +0 -889
- package/src/components/GraphQLExplorer.tsx +0 -675
- package/src/components/Icons.tsx +0 -23
- package/src/components/StatusBadge.tsx +0 -76
- package/src/lib/MediaService.ts +0 -541
- package/src/lib/auth/sqlite-adapter.ts +0 -319
- package/src/lib/dataStore.ts +0 -226
- package/src/lib/db/adapter.ts +0 -54
- package/src/lib/db/drizzle-mysql-adapter.ts +0 -194
- package/src/lib/db/drizzle-mysql-auth-adapter.ts +0 -327
- package/src/lib/db/drizzle-postgres-adapter.ts +0 -202
- package/src/lib/db/drizzle-postgres-auth-adapter.ts +0 -304
- package/src/lib/db/drizzle-sqlite-adapter.ts +0 -227
- package/src/lib/db/drizzle-sqlite-auth-adapter.ts +0 -548
- package/src/lib/db/index.ts +0 -449
- package/src/lib/db/mongodb-adapter.ts +0 -207
- package/src/lib/db/mongodb-auth-adapter.ts +0 -305
- package/src/lib/db/schema/mysql-auth.ts +0 -113
- package/src/lib/db/schema/mysql-content.ts +0 -20
- package/src/lib/db/schema/postgres-auth.ts +0 -116
- package/src/lib/db/schema/postgres-content.ts +0 -35
- package/src/lib/db/schema/postgres-media.ts +0 -52
- package/src/lib/db/schema/postgres-settings.ts +0 -11
- package/src/lib/db/schema/sqlite-auth.ts +0 -112
- package/src/lib/db/schema/sqlite-content.ts +0 -20
- package/src/lib/db/version-adapter.ts +0 -248
- package/src/lib/graphql/index.ts +0 -1
- package/src/lib/graphql/schema.ts +0 -443
- package/src/lib/rate-limit.ts +0 -267
- package/src/lib/storage.ts +0 -374
- package/src/lib/store.ts +0 -85
- package/src/middleware.ts +0 -177
- package/src/pages/admin/api-explorer.astro +0 -98
- package/src/pages/admin/graphql-explorer.astro +0 -40
- package/src/pages/admin/index.astro +0 -286
- package/src/pages/admin/keys.astro +0 -8
- package/src/pages/admin/rest-playground.astro +0 -44
- package/src/pages/admin/webhooks.astro +0 -8
- package/src/pages/api/[collection]/[id]/publish.ts +0 -52
- package/src/pages/api/[collection]/[id]/unpublish.ts +0 -42
- package/src/pages/api/[collection]/[id]/versions.ts +0 -66
- package/src/pages/api/[collection]/[id].ts +0 -213
- package/src/pages/api/[collection]/index.ts +0 -209
- package/src/pages/api/auth/[id].ts +0 -121
- package/src/pages/api/auth/audit-logs.ts +0 -57
- package/src/pages/api/auth/login.ts +0 -211
- package/src/pages/api/auth/logout.ts +0 -66
- package/src/pages/api/auth/me.ts +0 -36
- package/src/pages/api/auth/refresh.ts +0 -119
- package/src/pages/api/auth/register.ts +0 -188
- package/src/pages/api/auth/users.ts +0 -97
- package/src/pages/api/collections.ts +0 -59
- package/src/pages/api/globals/[slug].ts +0 -42
- package/src/pages/api/graphql.ts +0 -90
- package/src/pages/api/health.ts +0 -426
- package/src/pages/api/keys/[id].ts +0 -26
- package/src/pages/api/keys/index.ts +0 -75
- package/src/pages/api/media/[id].ts +0 -309
- package/src/pages/api/media/folders.ts +0 -609
- package/src/pages/api/media/index.ts +0 -146
- package/src/pages/api/media/resize.ts +0 -267
- package/src/pages/api/search.ts +0 -82
- package/src/pages/api/slug-availability.ts +0 -70
- package/src/pages/api/storage-config.ts +0 -20
- package/src/pages/api/storage-status.ts +0 -206
- package/src/pages/api/upload.ts +0 -334
- package/src/pages/api/webhooks/index.ts +0 -71
- package/src/pages/login.astro +0 -82
- package/src/pages/register.astro +0 -102
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
2
|
import { apiGet, apiPost, apiPatch, apiDelete } from "../lib/api";
|
|
3
|
-
import {
|
|
3
|
+
import { useResourceManager } from "../lib/useResourceManager";
|
|
4
|
+
|
|
4
5
|
import {
|
|
5
6
|
Webhook,
|
|
6
7
|
Plus,
|
|
@@ -16,8 +17,12 @@ import {
|
|
|
16
17
|
ExternalLink,
|
|
17
18
|
Zap,
|
|
18
19
|
Shield,
|
|
19
|
-
} from "
|
|
20
|
-
import {
|
|
20
|
+
} from "./ui/icons";
|
|
21
|
+
import { useUIStore, toast } from "../lib/stores";
|
|
22
|
+
import { Modal, ModalContent, ModalActions } from "./ui/Modal";
|
|
23
|
+
import { Badge } from "./ui/Badge";
|
|
24
|
+
import { PageHeader } from "./ui/PageHeader";
|
|
25
|
+
|
|
21
26
|
|
|
22
27
|
interface WebhookItem {
|
|
23
28
|
id: string;
|
|
@@ -31,17 +36,25 @@ interface WebhookItem {
|
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
export function WebhookManager() {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
const {
|
|
40
|
+
items: webhooks,
|
|
41
|
+
loading,
|
|
42
|
+
create,
|
|
43
|
+
remove,
|
|
44
|
+
update,
|
|
45
|
+
isCreateModalOpen: showCreateModal,
|
|
46
|
+
setIsCreateModalOpen: setShowCreateModal,
|
|
47
|
+
} = useResourceManager<WebhookItem>({
|
|
48
|
+
endpoint: "/api/webhooks",
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const { alert } = useUIStore();
|
|
38
52
|
const [showTestModal, setShowTestModal] = useState(false);
|
|
39
53
|
const [showHelpModal, setShowHelpModal] = useState(false);
|
|
40
54
|
const [testResult, setTestResult] = useState<{
|
|
41
55
|
success: boolean;
|
|
42
56
|
message: string;
|
|
43
57
|
} | null>(null);
|
|
44
|
-
const [deleteId, setDeleteId] = useState<string | null>(null);
|
|
45
58
|
const [testId, setTestId] = useState<string | null>(null);
|
|
46
59
|
const [formData, setFormData] = useState({
|
|
47
60
|
name: "",
|
|
@@ -51,22 +64,6 @@ export function WebhookManager() {
|
|
|
51
64
|
});
|
|
52
65
|
const [createError, setCreateError] = useState("");
|
|
53
66
|
|
|
54
|
-
const loadWebhooks = async () => {
|
|
55
|
-
setLoading(true);
|
|
56
|
-
try {
|
|
57
|
-
const data = await apiGet("/api/webhooks");
|
|
58
|
-
setWebhooks(data);
|
|
59
|
-
} catch (e) {
|
|
60
|
-
console.error(e);
|
|
61
|
-
} finally {
|
|
62
|
-
setLoading(false);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
loadWebhooks();
|
|
68
|
-
}, []);
|
|
69
|
-
|
|
70
67
|
const handleCreate = async () => {
|
|
71
68
|
if (!formData.name.trim() || !formData.url.trim()) {
|
|
72
69
|
setCreateError("Name and URL are required");
|
|
@@ -74,45 +71,26 @@ export function WebhookManager() {
|
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
try {
|
|
77
|
-
await
|
|
78
|
-
setShowCreateModal(false);
|
|
74
|
+
await create(formData);
|
|
79
75
|
setFormData({ name: "", url: "", events: [], secret: "" });
|
|
80
|
-
|
|
76
|
+
toast.success(`Webhook established: ${formData.name}`);
|
|
81
77
|
} catch (e) {
|
|
82
|
-
console.error(e);
|
|
83
78
|
setCreateError("Failed to create webhook");
|
|
84
79
|
}
|
|
85
80
|
};
|
|
86
81
|
|
|
87
|
-
const handleDelete = async (id: string) => {
|
|
88
|
-
setDeleteId(id);
|
|
89
|
-
setShowDeleteModal(true);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
const confirmDelete = async () => {
|
|
93
|
-
if (!deleteId) return;
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
await apiDelete(`/api/webhooks/${deleteId}`);
|
|
97
|
-
loadWebhooks();
|
|
98
|
-
} catch (e) {
|
|
99
|
-
console.error(e);
|
|
100
|
-
}
|
|
101
|
-
setShowDeleteModal(false);
|
|
102
|
-
setDeleteId(null);
|
|
103
|
-
};
|
|
104
82
|
|
|
105
83
|
const handleTest = async (id: string) => {
|
|
106
84
|
setTestId(id);
|
|
107
85
|
setTestResult(null);
|
|
108
86
|
setShowTestModal(true);
|
|
109
|
-
|
|
110
87
|
try {
|
|
111
|
-
const data = await apiPost(`/api/webhooks/${id}/test`);
|
|
88
|
+
const data = await apiPost<any>(`/api/webhooks/${id}/test`);
|
|
112
89
|
setTestResult({
|
|
113
90
|
success: true,
|
|
114
91
|
message: data.message || "Webhook triggered successfully",
|
|
115
92
|
});
|
|
93
|
+
toast.success("Dispatch verified: Remote endpoint responded");
|
|
116
94
|
} catch (e) {
|
|
117
95
|
setTestResult({ success: false, message: "Failed to trigger webhook" });
|
|
118
96
|
}
|
|
@@ -120,10 +98,11 @@ export function WebhookManager() {
|
|
|
120
98
|
|
|
121
99
|
const toggleStatus = async (id: string, currentStatus: string) => {
|
|
122
100
|
try {
|
|
123
|
-
|
|
124
|
-
|
|
101
|
+
const newStatus = currentStatus === "active" ? "paused" : "active";
|
|
102
|
+
await update(id, {
|
|
103
|
+
status: newStatus,
|
|
125
104
|
});
|
|
126
|
-
|
|
105
|
+
toast.success(newStatus === "active" ? "Signals resumed" : "Dispatcher paused");
|
|
127
106
|
} catch (e) {
|
|
128
107
|
console.error(e);
|
|
129
108
|
}
|
|
@@ -149,61 +128,59 @@ export function WebhookManager() {
|
|
|
149
128
|
];
|
|
150
129
|
|
|
151
130
|
return (
|
|
152
|
-
<div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
className="flex items-center gap-2 px-6 py-3 bg-[var(--kyro-sidebar-active)] text-[var(--kyro-sidebar-text-active)] rounded-full font-black text-sm shadow-xl hover:opacity-90 active:scale-95 transition-all"
|
|
181
|
-
>
|
|
182
|
-
<Plus className="w-4 h-4" />
|
|
183
|
-
Add Webhook
|
|
184
|
-
</button>
|
|
185
|
-
</div>
|
|
131
|
+
<div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 pb-32">
|
|
132
|
+
<PageHeader
|
|
133
|
+
title="Webhooks"
|
|
134
|
+
description="Receive real-time notifications when your content changes."
|
|
135
|
+
icon={Webhook}
|
|
136
|
+
actions={[
|
|
137
|
+
{
|
|
138
|
+
label: "Create Webhook",
|
|
139
|
+
onClick: () => {
|
|
140
|
+
setFormData({
|
|
141
|
+
name: "",
|
|
142
|
+
url: "",
|
|
143
|
+
events: ["collection.create", "collection.update", "collection.delete"],
|
|
144
|
+
secret: "",
|
|
145
|
+
});
|
|
146
|
+
setCreateError("");
|
|
147
|
+
setShowCreateModal(true);
|
|
148
|
+
},
|
|
149
|
+
icon: Plus,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
label: "Guide",
|
|
153
|
+
onClick: () => setShowHelpModal(true),
|
|
154
|
+
icon: Info,
|
|
155
|
+
variant: "outline"
|
|
156
|
+
}
|
|
157
|
+
]}
|
|
158
|
+
/>
|
|
186
159
|
|
|
187
|
-
{/* Info Banner */}
|
|
188
|
-
<div className="
|
|
189
|
-
<div className="
|
|
190
|
-
<
|
|
191
|
-
|
|
160
|
+
{/* Info Banner - Modernized */}
|
|
161
|
+
<div className="relative overflow-hidden rounded-[2rem] border border-[var(--kyro-border)] bg-gradient-to-br from-[var(--kyro-surface)] to-[var(--kyro-surface-accent)] p-8">
|
|
162
|
+
<div className="absolute top-0 right-0 p-8 opacity-[0.03] pointer-events-none">
|
|
163
|
+
<Zap className="w-64 h-64 rotate-12" />
|
|
164
|
+
</div>
|
|
165
|
+
<div className="flex flex-col md:flex-row items-center gap-8 relative z-10">
|
|
166
|
+
<div className="p-5 bg-gradient-to-br from-[var(--kyro-primary)] to-[var(--kyro-primary)]/50 rounded-[1.5rem] shadow-xl shadow-[var(--kyro-primary)]/20">
|
|
167
|
+
<Zap className="w-8 h-8 text-white" />
|
|
192
168
|
</div>
|
|
193
|
-
<div className="flex-1">
|
|
194
|
-
<h3 className="text-
|
|
195
|
-
<p className="text-sm text-[var(--kyro-text-secondary)] opacity-70
|
|
196
|
-
Webhooks allow your application to receive
|
|
197
|
-
|
|
198
|
-
the API, your endpoint gets notified immediately.
|
|
169
|
+
<div className="flex-1 text-center md:text-left">
|
|
170
|
+
<h3 className="text-xl font-bold mb-2">Real-time Event Synchronization</h3>
|
|
171
|
+
<p className="text-sm text-[var(--kyro-text-secondary)] opacity-70 max-w-2xl leading-relaxed">
|
|
172
|
+
Webhooks allow your application to receive instant HTTP notifications when events happen in your CMS.
|
|
173
|
+
Eliminate polling and build responsive, event-driven architectures with ease.
|
|
199
174
|
</p>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
175
|
+
</div>
|
|
176
|
+
<div className="flex gap-4">
|
|
177
|
+
<div className="flex flex-col items-center px-4 py-2 bg-[var(--kyro-surface)]/50 border border-[var(--kyro-border)] rounded-2xl">
|
|
178
|
+
<span className="text-[10px] font-bold opacity-40 uppercase tracking-tighter">Collections</span>
|
|
179
|
+
<span className="text-sm font-bold text-[var(--kyro-primary)]">Triggered</span>
|
|
180
|
+
</div>
|
|
181
|
+
<div className="flex flex-col items-center px-4 py-2 bg-[var(--kyro-surface)]/50 border border-[var(--kyro-border)] rounded-2xl">
|
|
182
|
+
<span className="text-[10px] font-bold opacity-40 uppercase tracking-tighter">Latency</span>
|
|
183
|
+
<span className="text-sm font-bold text-[var(--kyro-primary)]"><200ms</span>
|
|
207
184
|
</div>
|
|
208
185
|
</div>
|
|
209
186
|
</div>
|
|
@@ -211,31 +188,36 @@ export function WebhookManager() {
|
|
|
211
188
|
|
|
212
189
|
{/* Webhooks List */}
|
|
213
190
|
<section className="space-y-6">
|
|
214
|
-
<div className="flex items-center
|
|
215
|
-
<
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
</
|
|
191
|
+
<div className="flex items-center justify-between px-2">
|
|
192
|
+
<div className="flex items-center gap-2">
|
|
193
|
+
<div className="w-1 h-4 bg-[var(--kyro-primary)] rounded-full" />
|
|
194
|
+
<h2 className="text-sm font-medium tracking-[0.2em] opacity-40">ACTIVE ENDPOINTS</h2>
|
|
195
|
+
</div>
|
|
196
|
+
<div className="text-[10px] font-bold opacity-40 uppercase">
|
|
197
|
+
{webhooks.length} Hook{webhooks.length !== 1 && "s"}
|
|
198
|
+
</div>
|
|
219
199
|
</div>
|
|
220
200
|
|
|
221
201
|
{loading ? (
|
|
222
|
-
<div className="surface-tile
|
|
202
|
+
<div className="flex items-center justify-center p-20 surface-tile rounded-3xl opacity-50 italic">
|
|
203
|
+
Connecting to dispatcher...
|
|
204
|
+
</div>
|
|
223
205
|
) : webhooks.length === 0 ? (
|
|
224
|
-
<div className="
|
|
225
|
-
<div className="w-
|
|
226
|
-
<Webhook className="w-
|
|
206
|
+
<div className="p-16 text-center rounded-[3rem] border-2 border-dashed border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)]/30">
|
|
207
|
+
<div className="w-20 h-20 mx-auto mb-6 bg-gradient-to-tr from-[var(--kyro-surface)] to-[var(--kyro-surface-accent)] rounded-3xl flex items-center justify-center shadow-xl border border-[var(--kyro-border)]">
|
|
208
|
+
<Webhook className="w-10 h-10 text-[var(--kyro-primary)]" />
|
|
227
209
|
</div>
|
|
228
|
-
<h3 className="text-
|
|
229
|
-
<p className="text-sm text-[var(--kyro-text-secondary)] opacity-60 mb-
|
|
230
|
-
|
|
210
|
+
<h3 className="text-2xl font-bold mb-3">No Webhooks Found</h3>
|
|
211
|
+
<p className="text-sm text-[var(--kyro-text-secondary)] opacity-60 mb-8 max-w-sm mx-auto">
|
|
212
|
+
Your CMS is currently silent. Create a webhook to start broadcasting events to your external services.
|
|
231
213
|
</p>
|
|
232
214
|
<button
|
|
233
215
|
type="button"
|
|
234
216
|
onClick={() => setShowCreateModal(true)}
|
|
235
|
-
className="px-
|
|
217
|
+
className="inline-flex items-center gap-3 px-8 py-4 bg-[var(--kyro-primary)] text-white rounded-2xl font-bold hover:scale-[1.05] transition-all shadow-xl shadow-[var(--kyro-primary)]/10"
|
|
236
218
|
>
|
|
237
|
-
<Plus className="w-
|
|
238
|
-
|
|
219
|
+
<Plus className="w-5 h-5" />
|
|
220
|
+
Configure Webhook
|
|
239
221
|
</button>
|
|
240
222
|
</div>
|
|
241
223
|
) : (
|
|
@@ -243,98 +225,86 @@ export function WebhookManager() {
|
|
|
243
225
|
{webhooks.map((webhook) => (
|
|
244
226
|
<div
|
|
245
227
|
key={webhook.id}
|
|
246
|
-
className="surface-
|
|
228
|
+
className="group relative overflow-hidden bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-3xl p-6 hover:border-[var(--kyro-primary)]/50 transition-all duration-300"
|
|
247
229
|
>
|
|
248
|
-
<div className="flex items-
|
|
230
|
+
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-6 relative z-10">
|
|
249
231
|
<div className="flex-1">
|
|
250
|
-
<div className="flex items-center gap-3 mb-
|
|
251
|
-
<
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
{webhook.url}
|
|
264
|
-
</p>
|
|
265
|
-
<div className="flex items-center gap-2 flex-wrap mb-4">
|
|
266
|
-
{webhook.events?.map((event) => (
|
|
267
|
-
<span
|
|
268
|
-
key={event}
|
|
269
|
-
className="px-3 py-1 bg-[var(--kyro-surface-accent)] rounded-lg text-[10px] font-black uppercase opacity-60"
|
|
270
|
-
>
|
|
271
|
-
{event}
|
|
272
|
-
</span>
|
|
273
|
-
))}
|
|
232
|
+
<div className="flex items-center gap-3 mb-4">
|
|
233
|
+
<div className="p-2.5 bg-[var(--kyro-surface-accent)] rounded-xl group-hover:bg-[var(--kyro-primary)]/10 transition-colors">
|
|
234
|
+
<Webhook className="w-5 h-5 text-[var(--kyro-text-secondary)] group-hover:text-[var(--kyro-primary)] transition-colors" />
|
|
235
|
+
</div>
|
|
236
|
+
<div>
|
|
237
|
+
<h3 className="text-lg font-bold group-hover:text-[var(--kyro-primary)] transition-colors">{webhook.name}</h3>
|
|
238
|
+
<div className="flex items-center gap-2 mt-0.5">
|
|
239
|
+
<span className={`w-2 h-2 rounded-full ${webhook.status === "active" ? "bg-green-500 animate-pulse" : "bg-amber-500"}`} />
|
|
240
|
+
<span className={`text-[10px] font-bold uppercase tracking-wider ${webhook.status === "active" ? "text-green-500" : "text-amber-500"}`}>
|
|
241
|
+
{webhook.status}
|
|
242
|
+
</span>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
274
245
|
</div>
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
246
|
+
|
|
247
|
+
<div className="grid sm:grid-cols-3 gap-6 pt-2">
|
|
248
|
+
<div className="space-y-1">
|
|
249
|
+
<span className="text-[9px] font-bold uppercase tracking-widest opacity-30">Destination</span>
|
|
250
|
+
<div className="font-mono text-xs opacity-60 truncate max-w-[200px]" title={webhook.url}>
|
|
251
|
+
{webhook.url}
|
|
252
|
+
</div>
|
|
281
253
|
</div>
|
|
282
|
-
<div className="
|
|
283
|
-
<
|
|
284
|
-
|
|
285
|
-
|
|
254
|
+
<div className="space-y-1 border-l border-[var(--kyro-border)] pl-6">
|
|
255
|
+
<span className="text-[9px] font-bold uppercase tracking-widest opacity-30">Events</span>
|
|
256
|
+
<div className="flex flex-wrap gap-1">
|
|
257
|
+
{webhook.events.slice(0, 2).map((event) => (
|
|
258
|
+
<Badge key={event} variant="outline" className="text-[8px] font-bold px-1.5 opacity-60">
|
|
259
|
+
{event}
|
|
260
|
+
</Badge>
|
|
261
|
+
))}
|
|
262
|
+
{webhook.events.length > 2 && (
|
|
263
|
+
<span className="text-[8px] font-bold opacity-30">+{webhook.events.length - 2} more</span>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
<div className="space-y-1 border-l border-[var(--kyro-border)] pl-6">
|
|
268
|
+
<span className="text-[9px] font-bold uppercase tracking-widest opacity-30">Activity</span>
|
|
269
|
+
<div className="text-[10px] font-bold opacity-60 flex items-center gap-1.5">
|
|
270
|
+
<Clock className="w-3 h-3" />
|
|
271
|
+
{webhook.lastTriggered
|
|
272
|
+
? `Last triggered: ${new Date(webhook.lastTriggered).toLocaleDateString()}`
|
|
273
|
+
: "Never triggered"}
|
|
274
|
+
</div>
|
|
286
275
|
</div>
|
|
287
276
|
</div>
|
|
288
277
|
</div>
|
|
289
|
-
|
|
278
|
+
|
|
279
|
+
<div className="flex items-center gap-2 lg:bg-[var(--kyro-surface-accent)]/50 lg:p-2 lg:rounded-2xl lg:opacity-0 group-hover:opacity-100 transition-all duration-300 lg:translate-x-4 group-hover:translate-x-0">
|
|
290
280
|
<button
|
|
291
281
|
type="button"
|
|
292
282
|
onClick={() => handleTest(webhook.id)}
|
|
293
|
-
className="p-3 bg-[var(--kyro-surface
|
|
283
|
+
className="flex-1 lg:flex-none p-3 bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-xl hover:border-[var(--kyro-primary)] transition-all flex items-center justify-center gap-2"
|
|
294
284
|
title="Send test request"
|
|
295
285
|
>
|
|
296
286
|
<Send className="w-4 h-4" />
|
|
297
|
-
<span className="text-xs font-
|
|
298
|
-
Test
|
|
299
|
-
</span>
|
|
287
|
+
<span className="text-xs font-bold">Test</span>
|
|
300
288
|
</button>
|
|
289
|
+
|
|
290
|
+
<div className="w-px h-6 bg-[var(--kyro-border)] mx-1 hidden lg:block" />
|
|
291
|
+
|
|
301
292
|
<button
|
|
302
293
|
type="button"
|
|
303
294
|
onClick={() => toggleStatus(webhook.id, webhook.status)}
|
|
304
|
-
className={`p-3 rounded-xl
|
|
305
|
-
|
|
306
|
-
? "text-amber-500 bg-amber-500/10 hover:bg-amber-500/20"
|
|
307
|
-
: "text-green-500 bg-green-500/10 hover:bg-green-500/20"
|
|
308
|
-
}`}
|
|
309
|
-
title={
|
|
310
|
-
webhook.status === "active"
|
|
311
|
-
? "Pause webhook"
|
|
312
|
-
: "Activate webhook"
|
|
313
|
-
}
|
|
295
|
+
className={`p-3 bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-xl hover:border-[var(--kyro-primary)] transition-all group/toggle ${webhook.status === "active" ? "text-amber-500/50 hover:text-amber-500" : "text-green-500/50 hover:text-green-500"}`}
|
|
296
|
+
title={webhook.status === "active" ? "Pause webhook" : "Activate webhook"}
|
|
314
297
|
>
|
|
315
|
-
{webhook.status === "active" ?
|
|
316
|
-
<>
|
|
317
|
-
<Pause className="w-4 h-4" />
|
|
318
|
-
<span className="text-xs font-medium hidden lg:inline">
|
|
319
|
-
Pause
|
|
320
|
-
</span>
|
|
321
|
-
</>
|
|
322
|
-
) : (
|
|
323
|
-
<>
|
|
324
|
-
<Play className="w-4 h-4" />
|
|
325
|
-
<span className="text-xs font-medium hidden lg:inline">
|
|
326
|
-
Activate
|
|
327
|
-
</span>
|
|
328
|
-
</>
|
|
329
|
-
)}
|
|
298
|
+
{webhook.status === "active" ? <Pause className="w-4 h-4" /> : <Play className="w-4 h-4" />}
|
|
330
299
|
</button>
|
|
300
|
+
|
|
331
301
|
<button
|
|
332
302
|
type="button"
|
|
333
|
-
onClick={() =>
|
|
334
|
-
className="p-3
|
|
303
|
+
onClick={() => remove(webhook.id, "Webhook")}
|
|
304
|
+
className="p-3 bg-red-500/5 border border-red-500/10 rounded-xl hover:bg-red-500/10 hover:border-red-500/30 transition-all group/delete"
|
|
335
305
|
title="Delete webhook"
|
|
336
306
|
>
|
|
337
|
-
<Trash2 className="w-4 h-4" />
|
|
307
|
+
<Trash2 className="w-4 h-4 text-red-500/50 group-hover/delete:text-red-500" />
|
|
338
308
|
</button>
|
|
339
309
|
</div>
|
|
340
310
|
</div>
|
|
@@ -348,89 +318,87 @@ export function WebhookManager() {
|
|
|
348
318
|
<Modal
|
|
349
319
|
open={showCreateModal}
|
|
350
320
|
onClose={() => setShowCreateModal(false)}
|
|
351
|
-
title="
|
|
321
|
+
title="Register Webhook"
|
|
322
|
+
size="lg"
|
|
352
323
|
>
|
|
353
324
|
<ModalContent>
|
|
354
|
-
<div className="space-y-
|
|
355
|
-
<div>
|
|
356
|
-
<
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
325
|
+
<div className="space-y-6">
|
|
326
|
+
<div className="flex flex-col gap-6">
|
|
327
|
+
<div className="space-y-4">
|
|
328
|
+
<div>
|
|
329
|
+
<label className="block text-xs font-bold mb-1.5 text-[var(--kyro-text-secondary)] uppercase tracking-wider">Webhook Name</label>
|
|
330
|
+
<input
|
|
331
|
+
type="text"
|
|
332
|
+
value={formData.name}
|
|
333
|
+
onChange={(e) =>
|
|
334
|
+
setFormData({ ...formData, name: e.target.value })
|
|
335
|
+
}
|
|
336
|
+
placeholder="e.g., Slack Notifications"
|
|
337
|
+
className="w-full px-4 py-3 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-xl text-[var(--kyro-text-primary)] focus:outline-none focus:border-[var(--kyro-primary)]"
|
|
338
|
+
/>
|
|
339
|
+
</div>
|
|
340
|
+
<div>
|
|
341
|
+
<label className="block text-xs font-bold mb-1.5 text-[var(--kyro-text-secondary)] uppercase tracking-wider">Payload URL</label>
|
|
342
|
+
<input
|
|
343
|
+
type="url"
|
|
344
|
+
value={formData.url}
|
|
345
|
+
onChange={(e) =>
|
|
346
|
+
setFormData({ ...formData, url: e.target.value })
|
|
347
|
+
}
|
|
348
|
+
placeholder="https://your-server.com/webhook"
|
|
349
|
+
className="w-full px-4 py-3 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-xl text-[var(--kyro-text-primary)] focus:outline-none focus:border-[var(--kyro-primary)]"
|
|
350
|
+
/>
|
|
351
|
+
</div>
|
|
352
|
+
<div>
|
|
353
|
+
<label className="block text-xs font-bold mb-1.5 text-[var(--kyro-text-secondary)] uppercase tracking-wider">Signing Secret</label>
|
|
354
|
+
<input
|
|
355
|
+
type="text"
|
|
356
|
+
value={formData.secret}
|
|
357
|
+
onChange={(e) =>
|
|
358
|
+
setFormData({ ...formData, secret: e.target.value })
|
|
359
|
+
}
|
|
360
|
+
placeholder="Optional secret for verification"
|
|
361
|
+
className="w-full px-4 py-3 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-xl text-[var(--kyro-text-primary)] focus:outline-none focus:border-[var(--kyro-primary)]"
|
|
362
|
+
/>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<div className="space-y-4">
|
|
367
|
+
<label className="block text-xs font-bold mb-1.5 text-[var(--kyro-text-secondary)] uppercase tracking-wider">Subscribed Events</label>
|
|
368
|
+
<div className="grid grid-cols-2 gap-4">
|
|
369
|
+
{eventOptions.map((opt) => (
|
|
370
|
+
<button
|
|
371
|
+
type="button"
|
|
372
|
+
key={opt.value}
|
|
373
|
+
onClick={() => {
|
|
374
|
+
const events = formData.events.includes(opt.value)
|
|
375
|
+
? formData.events.filter((e) => e !== opt.value)
|
|
376
|
+
: [...formData.events, opt.value];
|
|
377
|
+
setFormData({ ...formData, events });
|
|
378
|
+
}}
|
|
379
|
+
className={`flex items-center gap-3 p-3 rounded-lg border transition-all text-left ${formData.events.includes(opt.value)
|
|
380
|
+
? "bg-[var(--kyro-primary)]/5 border-[var(--kyro-primary)]/30 text-[var(--kyro-primary)]"
|
|
381
|
+
: "bg-[var(--kyro-surface-accent)]/50 border-[var(--kyro-border)] hover:border-[var(--kyro-primary)]/30"
|
|
382
|
+
}`}
|
|
383
|
+
>
|
|
384
|
+
<div className={`w-4 h-4 rounded flex items-center justify-center border transition-all ${formData.events.includes(opt.value) ? "bg-[var(--kyro-primary)] border-[var(--kyro-primary)]" : "border-[var(--kyro-border)]"}`}>
|
|
385
|
+
{formData.events.includes(opt.value) && <CheckCircle2 className="w-3 h-3 text-white" />}
|
|
386
|
+
</div>
|
|
387
|
+
<div>
|
|
388
|
+
<div className="text-xs font-bold">{opt.label}</div>
|
|
389
|
+
<div className="text-[10px] opacity-50">{opt.description}</div>
|
|
390
|
+
</div>
|
|
391
|
+
</button>
|
|
392
|
+
))}
|
|
393
|
+
</div>
|
|
413
394
|
</div>
|
|
414
395
|
</div>
|
|
415
|
-
|
|
416
|
-
<label className="block text-sm font-medium mb-2">
|
|
417
|
-
Signing Secret (optional)
|
|
418
|
-
</label>
|
|
419
|
-
<input
|
|
420
|
-
type="text"
|
|
421
|
-
value={formData.secret}
|
|
422
|
-
onChange={(e) =>
|
|
423
|
-
setFormData({ ...formData, secret: e.target.value })
|
|
424
|
-
}
|
|
425
|
-
placeholder="Your webhook signing secret"
|
|
426
|
-
className="w-full px-4 py-3 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-xl focus:outline-none focus:border-[var(--kyro-primary)]"
|
|
427
|
-
/>
|
|
428
|
-
<p className="text-xs text-[var(--kyro-text-secondary)] mt-1 opacity-60">
|
|
429
|
-
Used to verify that requests came from Kyro CMS.
|
|
430
|
-
</p>
|
|
431
|
-
</div>
|
|
396
|
+
|
|
432
397
|
{createError && (
|
|
433
|
-
<
|
|
398
|
+
<div className="p-3 bg-red-500/10 border border-red-500/20 rounded-xl flex items-center gap-2 text-red-500 text-xs font-bold">
|
|
399
|
+
<AlertTriangle className="w-4 h-4" />
|
|
400
|
+
{createError}
|
|
401
|
+
</div>
|
|
434
402
|
)}
|
|
435
403
|
</div>
|
|
436
404
|
</ModalContent>
|
|
@@ -438,95 +406,58 @@ export function WebhookManager() {
|
|
|
438
406
|
<button
|
|
439
407
|
type="button"
|
|
440
408
|
onClick={() => setShowCreateModal(false)}
|
|
441
|
-
className="px-
|
|
409
|
+
className="px-6 py-2.5 rounded-xl font-bold text-sm border border-[var(--kyro-border)] text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-surface-accent)] transition-colors"
|
|
442
410
|
>
|
|
443
411
|
Cancel
|
|
444
412
|
</button>
|
|
445
413
|
<button
|
|
446
414
|
type="button"
|
|
447
415
|
onClick={handleCreate}
|
|
448
|
-
className="px-
|
|
416
|
+
className="px-6 py-2.5 rounded-xl font-bold text-sm bg-[var(--kyro-primary)] text-white hover:opacity-90 transition-all shadow-lg shadow-[var(--kyro-primary)]/10"
|
|
449
417
|
>
|
|
450
418
|
Create Webhook
|
|
451
419
|
</button>
|
|
452
420
|
</ModalActions>
|
|
453
421
|
</Modal>
|
|
454
422
|
|
|
455
|
-
{/* Delete Modal */}
|
|
456
|
-
<Modal
|
|
457
|
-
open={showDeleteModal}
|
|
458
|
-
onClose={() => setShowDeleteModal(false)}
|
|
459
|
-
title="Delete Webhook"
|
|
460
|
-
variant="danger"
|
|
461
|
-
>
|
|
462
|
-
<ModalContent>
|
|
463
|
-
<p className="text-sm text-[var(--kyro-text-secondary)] mb-4">
|
|
464
|
-
Are you sure you want to delete this webhook? This action cannot be
|
|
465
|
-
undone.
|
|
466
|
-
</p>
|
|
467
|
-
<div className="p-4 bg-red-500/10 border border-red-500/20 rounded-xl">
|
|
468
|
-
<p className="text-sm font-medium text-red-500">
|
|
469
|
-
This endpoint will stop receiving notifications immediately.
|
|
470
|
-
</p>
|
|
471
|
-
</div>
|
|
472
|
-
</ModalContent>
|
|
473
|
-
<ModalActions>
|
|
474
|
-
<button
|
|
475
|
-
type="button"
|
|
476
|
-
onClick={() => setShowDeleteModal(false)}
|
|
477
|
-
className="px-4 py-2 rounded-lg font-medium text-sm border border-[var(--kyro-border)] text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-surface-accent)]"
|
|
478
|
-
>
|
|
479
|
-
Keep Webhook
|
|
480
|
-
</button>
|
|
481
|
-
<button
|
|
482
|
-
type="button"
|
|
483
|
-
onClick={confirmDelete}
|
|
484
|
-
className="px-4 py-2 rounded-lg font-medium text-sm bg-red-500 text-white hover:bg-red-600"
|
|
485
|
-
>
|
|
486
|
-
Delete Permanently
|
|
487
|
-
</button>
|
|
488
|
-
</ModalActions>
|
|
489
|
-
</Modal>
|
|
490
|
-
|
|
491
423
|
{/* Test Webhook Modal */}
|
|
492
424
|
<Modal
|
|
493
425
|
open={showTestModal}
|
|
494
426
|
onClose={() => setShowTestModal(false)}
|
|
495
|
-
title="Test
|
|
427
|
+
title="Webhook Test Dispatcher"
|
|
496
428
|
>
|
|
497
429
|
<ModalContent>
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
className=
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
</p>
|
|
511
|
-
<p className="text-sm text-[var(--kyro-text-secondary)]">
|
|
512
|
-
{testResult.message}
|
|
513
|
-
</p>
|
|
430
|
+
<div className="p-8 rounded-[2rem] bg-[var(--kyro-surface-accent)]/30 border border-[var(--kyro-border)] text-center">
|
|
431
|
+
{testResult ? (
|
|
432
|
+
<div className="space-y-6">
|
|
433
|
+
<div className={`w-16 h-16 mx-auto rounded-2xl flex items-center justify-center shadow-xl ${testResult.success ? "bg-green-500/10 text-green-500" : "bg-red-500/10 text-red-500"}`}>
|
|
434
|
+
{testResult.success ? <CheckCircle2 className="w-8 h-8" /> : <AlertTriangle className="w-8 h-8" />}
|
|
435
|
+
</div>
|
|
436
|
+
<div>
|
|
437
|
+
<h4 className="text-xl font-bold mb-2">{testResult.success ? "Dispatch Successful" : "Dispatch Failed"}</h4>
|
|
438
|
+
<p className="text-sm text-[var(--kyro-text-secondary)] opacity-70">
|
|
439
|
+
{testResult.message}
|
|
440
|
+
</p>
|
|
441
|
+
</div>
|
|
514
442
|
</div>
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
443
|
+
) : (
|
|
444
|
+
<div className="space-y-6 py-4">
|
|
445
|
+
<RefreshCw className="w-12 h-12 text-[var(--kyro-primary)] animate-spin mx-auto opacity-50" />
|
|
446
|
+
<div>
|
|
447
|
+
<h4 className="text-xl font-bold mb-1">Synthesizing Payload</h4>
|
|
448
|
+
<p className="text-sm text-[var(--kyro-text-secondary)] opacity-50 italic">Dispatching to remote endpoint...</p>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
)}
|
|
452
|
+
</div>
|
|
522
453
|
</ModalContent>
|
|
523
454
|
<ModalActions>
|
|
524
455
|
<button
|
|
525
456
|
type="button"
|
|
526
457
|
onClick={() => setShowTestModal(false)}
|
|
527
|
-
className="
|
|
458
|
+
className="w-full py-3 rounded-xl font-bold text-sm bg-[var(--kyro-surface-accent)] border border-[var(--kyro-border)] hover:bg-[var(--kyro-surface)] transition-all"
|
|
528
459
|
>
|
|
529
|
-
|
|
460
|
+
Acknowledge
|
|
530
461
|
</button>
|
|
531
462
|
</ModalActions>
|
|
532
463
|
</Modal>
|
|
@@ -535,58 +466,65 @@ export function WebhookManager() {
|
|
|
535
466
|
<Modal
|
|
536
467
|
open={showHelpModal}
|
|
537
468
|
onClose={() => setShowHelpModal(false)}
|
|
538
|
-
title="
|
|
469
|
+
title="Webhook Integration Guide"
|
|
470
|
+
size="lg"
|
|
539
471
|
>
|
|
540
472
|
<ModalContent>
|
|
541
|
-
<div className="space-y-
|
|
542
|
-
<div>
|
|
543
|
-
<
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
<p className="text-sm text-[var(--kyro-text-secondary)] mb-3">
|
|
553
|
-
When an event triggers, Kyro sends a POST request with:
|
|
554
|
-
</p>
|
|
555
|
-
<div className="bg-[var(--kyro-bg)] rounded-lg p-4 font-mono text-sm space-y-2">
|
|
556
|
-
<div className="text-[var(--kyro-text-secondary)]">{"{"}</div>
|
|
557
|
-
<div className="pl-4">"event": "create",</div>
|
|
558
|
-
<div className="pl-4">"collection": "posts",</div>
|
|
559
|
-
<div className="pl-4">"documentId": "xxx",</div>
|
|
560
|
-
<div className="pl-4">"timestamp": "2024-..."</div>
|
|
561
|
-
<div className="text-[var(--kyro-text-secondary)]">{"}"}</div>
|
|
473
|
+
<div className="space-y-8">
|
|
474
|
+
<div className="grid grid-cols-1 gap-6">
|
|
475
|
+
<div className="p-6 rounded-3xl bg-[var(--kyro-surface-accent)]/50 border border-[var(--kyro-border)]">
|
|
476
|
+
<h4 className="font-bold mb-3 flex items-center gap-2">
|
|
477
|
+
<ExternalLink className="w-4 h-4 text-[var(--kyro-primary)]" />
|
|
478
|
+
Request Context
|
|
479
|
+
</h4>
|
|
480
|
+
<p className="text-sm text-[var(--kyro-text-secondary)] leading-relaxed">
|
|
481
|
+
When an event triggers, Kyro sends a standard <span className="font-bold text-[var(--kyro-text-primary)]">POST</span> request
|
|
482
|
+
to your endpoint with a JSON payload containing document metadata and operation details.
|
|
483
|
+
</p>
|
|
562
484
|
</div>
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
</div>
|
|
572
|
-
<div className="bg-amber-500/10 border border-amber-500/20 rounded-xl p-4">
|
|
573
|
-
<div className="flex items-start gap-2">
|
|
574
|
-
<Shield className="w-5 h-5 text-amber-500 flex-shrink-0" />
|
|
575
|
-
<p className="text-sm text-[var(--kyro-text-secondary)]">
|
|
576
|
-
Always verify webhook signatures in production to ensure
|
|
577
|
-
requests are authentic.
|
|
485
|
+
<div className="p-6 rounded-3xl bg-[var(--kyro-surface-accent)]/50 border border-[var(--kyro-border)]">
|
|
486
|
+
<h4 className="font-bold mb-3 flex items-center gap-2">
|
|
487
|
+
<Shield className="w-4 h-4 text-[var(--kyro-primary)]" />
|
|
488
|
+
Security Verification
|
|
489
|
+
</h4>
|
|
490
|
+
<p className="text-sm text-[var(--kyro-text-secondary)] leading-relaxed">
|
|
491
|
+
If a secret is provided, each request includes an <span className="font-mono text-[var(--kyro-primary)]">X-Kyro-Signature</span> header.
|
|
492
|
+
Always verify this signature in production environments.
|
|
578
493
|
</p>
|
|
579
494
|
</div>
|
|
580
495
|
</div>
|
|
496
|
+
|
|
497
|
+
<div className="space-y-3">
|
|
498
|
+
<h4 className="text-xs font-bold uppercase tracking-widest opacity-40 px-1">Payload Architecture</h4>
|
|
499
|
+
<div className="relative group">
|
|
500
|
+
<div className="absolute -inset-1 bg-gradient-to-r from-[var(--kyro-primary)]/20 to-transparent rounded-2xl blur opacity-25 group-hover:opacity-50 transition-all"></div>
|
|
501
|
+
<div className="relative bg-[var(--kyro-bg)] rounded-2xl border border-[var(--kyro-border)] p-6 font-mono text-sm overflow-hidden">
|
|
502
|
+
<div className="flex items-center gap-2 mb-4 border-b border-[var(--kyro-border)] pb-4 opacity-30">
|
|
503
|
+
<div className="w-2 h-2 rounded-full bg-red-500/50" />
|
|
504
|
+
<div className="w-2 h-2 rounded-full bg-amber-500/50" />
|
|
505
|
+
<div className="w-2 h-2 rounded-full bg-green-500/50" />
|
|
506
|
+
<span className="text-[10px] ml-2 tracking-tighter">payload.json</span>
|
|
507
|
+
</div>
|
|
508
|
+
<div className="space-y-1">
|
|
509
|
+
<div className="text-[var(--kyro-text-secondary)]">{"{"}</div>
|
|
510
|
+
<div className="pl-4"><span className="text-[var(--kyro-primary)]">"event"</span>: <span className="text-green-500">"collection.create"</span>,</div>
|
|
511
|
+
<div className="pl-4"><span className="text-[var(--kyro-primary)]">"collection"</span>: <span className="text-green-500">"products"</span>,</div>
|
|
512
|
+
<div className="pl-4"><span className="text-[var(--kyro-primary)]">"id"</span>: <span className="text-green-500">"prod_8273"</span>,</div>
|
|
513
|
+
<div className="pl-4"><span className="text-[var(--kyro-primary)]">"timestamp"</span>: <span className="text-green-500">"2026-05-14T02:53:22Z"</span></div>
|
|
514
|
+
<div className="text-[var(--kyro-text-secondary)]">{"}"}</div>
|
|
515
|
+
</div>
|
|
516
|
+
</div>
|
|
517
|
+
</div>
|
|
518
|
+
</div>
|
|
581
519
|
</div>
|
|
582
520
|
</ModalContent>
|
|
583
521
|
<ModalActions>
|
|
584
522
|
<button
|
|
585
523
|
type="button"
|
|
586
524
|
onClick={() => setShowHelpModal(false)}
|
|
587
|
-
className="
|
|
525
|
+
className="w-full py-3 rounded-xl font-bold text-sm bg-[var(--kyro-primary)] text-white hover:opacity-90 transition-all shadow-lg shadow-[var(--kyro-primary)]/20"
|
|
588
526
|
>
|
|
589
|
-
|
|
527
|
+
I Understand
|
|
590
528
|
</button>
|
|
591
529
|
</ModalActions>
|
|
592
530
|
</Modal>
|