@workspace-platform/ui 0.5.0 → 0.6.0

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/admin.mjs CHANGED
@@ -1 +1,1858 @@
1
+ import {
2
+ ConfirmModal,
3
+ Modal,
4
+ useWorkspaceApi
5
+ } from "./chunk-4UDSYAAD.mjs";
6
+ import "./chunk-UBQ42LPM.mjs";
7
+
8
+ // src/admin/WorkspaceTypesPage.tsx
9
+ import { useCallback, useEffect, useState } from "react";
10
+ import {
11
+ Plus,
12
+ Pencil,
13
+ Trash2,
14
+ Tag,
15
+ Loader2,
16
+ Clock,
17
+ User,
18
+ X,
19
+ ArrowLeft,
20
+ Lock,
21
+ Unlock
22
+ } from "lucide-react";
23
+ import { format } from "date-fns";
24
+ import { fr } from "date-fns/locale";
25
+ import { jsx, jsxs } from "react/jsx-runtime";
26
+ function WorkspaceTypesPage({ onBack }) {
27
+ const { workspaceTypeApi } = useWorkspaceApi();
28
+ const [types, setTypes] = useState([]);
29
+ const [loading, setLoading] = useState(true);
30
+ const [error, setError] = useState("");
31
+ const [showForm, setShowForm] = useState(false);
32
+ const [editTarget, setEditTarget] = useState(null);
33
+ const [formName, setFormName] = useState("");
34
+ const [formEntities, setFormEntities] = useState([]);
35
+ const [saving, setSaving] = useState(false);
36
+ const [selectedId, setSelectedId] = useState(null);
37
+ const [deleteId, setDeleteId] = useState(null);
38
+ const load = useCallback(async () => {
39
+ try {
40
+ setLoading(true);
41
+ setTypes(await workspaceTypeApi.list());
42
+ } catch (e) {
43
+ setError(e.message);
44
+ } finally {
45
+ setLoading(false);
46
+ }
47
+ }, [workspaceTypeApi]);
48
+ useEffect(() => {
49
+ load();
50
+ }, [load]);
51
+ const toFormEntities = (t) => {
52
+ if (t.entitiesWithSecrets && t.entitiesWithSecrets.length > 0) {
53
+ return t.entitiesWithSecrets.map((e) => ({
54
+ key: e.key,
55
+ value: e.value,
56
+ isSecret: e.isSecret
57
+ }));
58
+ }
59
+ return [];
60
+ };
61
+ const openCreate = () => {
62
+ setEditTarget(null);
63
+ setFormName("");
64
+ setFormEntities([]);
65
+ setShowForm(true);
66
+ };
67
+ const openEdit = (t) => {
68
+ setEditTarget(t);
69
+ setFormName(t.name);
70
+ setFormEntities(toFormEntities(t));
71
+ setShowForm(true);
72
+ };
73
+ const handleSubmit = async (e) => {
74
+ e.preventDefault();
75
+ if (!formName.trim()) return;
76
+ try {
77
+ setSaving(true);
78
+ const entitiesWithSecrets = formEntities.filter((ent) => ent.key.trim()).map((ent) => ({
79
+ key: ent.key.trim(),
80
+ value: ent.value,
81
+ isSecret: ent.isSecret
82
+ }));
83
+ if (editTarget) {
84
+ await workspaceTypeApi.update(editTarget._id, {
85
+ name: formName.trim(),
86
+ entitiesWithSecrets
87
+ });
88
+ } else {
89
+ await workspaceTypeApi.create({
90
+ name: formName.trim(),
91
+ entitiesWithSecrets: entitiesWithSecrets.length > 0 ? entitiesWithSecrets : void 0
92
+ });
93
+ }
94
+ setShowForm(false);
95
+ load();
96
+ } catch (e2) {
97
+ setError(e2.message);
98
+ } finally {
99
+ setSaving(false);
100
+ }
101
+ };
102
+ const confirmDelete = (id) => setDeleteId(id);
103
+ const executeDelete = async () => {
104
+ if (!deleteId) return;
105
+ try {
106
+ await workspaceTypeApi.delete(deleteId);
107
+ load();
108
+ } catch (e) {
109
+ setError(e.message);
110
+ }
111
+ };
112
+ const getDisplayEntities = (t) => {
113
+ return t.entitiesWithSecrets ?? [];
114
+ };
115
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col", children: [
116
+ /* @__PURE__ */ jsxs("div", { className: "bg-surface border-border flex shrink-0 items-center gap-4 border-b px-6 py-4", children: [
117
+ onBack && /* @__PURE__ */ jsx(
118
+ "button",
119
+ {
120
+ type: "button",
121
+ onClick: () => onBack(),
122
+ className: "text-text-muted hover:text-text hover:bg-bg rounded-lg p-1.5 transition",
123
+ children: /* @__PURE__ */ jsx(ArrowLeft, { size: 20 })
124
+ }
125
+ ),
126
+ /* @__PURE__ */ jsx("div", { className: "bg-primary-light text-primary flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ jsx(Tag, { size: 24 }) }),
127
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
128
+ /* @__PURE__ */ jsx("h1", { className: "text-text text-xl font-semibold", children: "Types de Workspace" }),
129
+ /* @__PURE__ */ jsx("p", { className: "text-text-secondary text-sm", children: "G\xE9rez les cat\xE9gories d'espaces de travail" })
130
+ ] }),
131
+ /* @__PURE__ */ jsxs(
132
+ "button",
133
+ {
134
+ type: "button",
135
+ onClick: openCreate,
136
+ className: "bg-primary hover:bg-primary-hover flex items-center gap-2 rounded-xl px-4 py-2.5 text-sm font-medium text-white transition",
137
+ children: [
138
+ /* @__PURE__ */ jsx(Plus, { size: 16 }),
139
+ " Nouveau type"
140
+ ]
141
+ }
142
+ )
143
+ ] }),
144
+ error && /* @__PURE__ */ jsxs("div", { className: "border-danger/30 bg-danger-light text-danger mx-6 mt-3 rounded-xl border px-4 py-3 text-sm", children: [
145
+ error,
146
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setError(""), className: "ml-2 underline", children: "\xD7" })
147
+ ] }),
148
+ /* @__PURE__ */ jsx(
149
+ ConfirmModal,
150
+ {
151
+ open: !!deleteId,
152
+ onClose: () => setDeleteId(null),
153
+ onConfirm: executeDelete,
154
+ title: "Supprimer ce type",
155
+ description: "Voulez-vous vraiment supprimer ce type de workspace ?",
156
+ danger: true
157
+ }
158
+ ),
159
+ showForm && /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4 backdrop-blur-sm", children: /* @__PURE__ */ jsxs("div", { className: "bg-surface border-border w-full max-w-xl overflow-x-hidden overflow-y-auto rounded-2xl border p-6 shadow-2xl", children: [
160
+ /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-between", children: [
161
+ /* @__PURE__ */ jsx("h2", { className: "text-text text-lg font-semibold", children: editTarget ? "Modifier le type" : "Cr\xE9er un type" }),
162
+ /* @__PURE__ */ jsx(
163
+ "button",
164
+ {
165
+ type: "button",
166
+ onClick: () => setShowForm(false),
167
+ className: "text-text-muted hover:text-text transition",
168
+ children: /* @__PURE__ */ jsx(X, { size: 20 })
169
+ }
170
+ )
171
+ ] }),
172
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
173
+ /* @__PURE__ */ jsxs("div", { children: [
174
+ /* @__PURE__ */ jsx("label", { className: "text-text-secondary mb-1.5 block text-sm", children: "Nom" }),
175
+ /* @__PURE__ */ jsx(
176
+ "input",
177
+ {
178
+ value: formName,
179
+ onChange: (e) => setFormName(e.target.value),
180
+ placeholder: "ex: incident, projet, client...",
181
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none",
182
+ autoFocus: true
183
+ }
184
+ )
185
+ ] }),
186
+ /* @__PURE__ */ jsxs("div", { children: [
187
+ /* @__PURE__ */ jsx("label", { className: "text-text-secondary mb-1.5 block text-sm", children: "Entit\xE9s" }),
188
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
189
+ formEntities.map((ent, idx) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
190
+ /* @__PURE__ */ jsx(
191
+ "input",
192
+ {
193
+ value: ent.key,
194
+ onChange: (e) => {
195
+ const updated = [...formEntities];
196
+ updated[idx].key = e.target.value;
197
+ setFormEntities(updated);
198
+ },
199
+ placeholder: "Cl\xE9",
200
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary flex-[1] rounded-xl border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
201
+ }
202
+ ),
203
+ /* @__PURE__ */ jsx(
204
+ "input",
205
+ {
206
+ value: ent.value,
207
+ onChange: (e) => {
208
+ const updated = [...formEntities];
209
+ updated[idx].value = e.target.value;
210
+ setFormEntities(updated);
211
+ },
212
+ placeholder: "Ex: noemie-service",
213
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary flex-[2] rounded-xl border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
214
+ }
215
+ ),
216
+ /* @__PURE__ */ jsx(
217
+ "button",
218
+ {
219
+ type: "button",
220
+ onClick: () => {
221
+ const updated = [...formEntities];
222
+ updated[idx].isSecret = !updated[idx].isSecret;
223
+ setFormEntities(updated);
224
+ },
225
+ className: `rounded-lg p-2 transition ${ent.isSecret ? "bg-warning/15 text-warning" : "text-text-muted hover:text-text hover:bg-bg"}`,
226
+ title: ent.isSecret ? "Secret (cliquer pour rendre public)" : "Public (cliquer pour rendre secret)",
227
+ children: ent.isSecret ? /* @__PURE__ */ jsx(Lock, { size: 16 }) : /* @__PURE__ */ jsx(Unlock, { size: 16 })
228
+ }
229
+ ),
230
+ /* @__PURE__ */ jsx(
231
+ "button",
232
+ {
233
+ type: "button",
234
+ onClick: () => setFormEntities(
235
+ (prev) => prev.filter((_, i) => i !== idx)
236
+ ),
237
+ className: "text-text-muted hover:text-danger p-2 transition",
238
+ children: /* @__PURE__ */ jsx(Trash2, { size: 16 })
239
+ }
240
+ )
241
+ ] }, idx)),
242
+ /* @__PURE__ */ jsxs(
243
+ "button",
244
+ {
245
+ type: "button",
246
+ onClick: () => setFormEntities((prev) => [
247
+ ...prev,
248
+ { key: "", value: "", isSecret: false }
249
+ ]),
250
+ className: "text-primary hover:text-primary-hover flex items-center gap-1 text-sm transition",
251
+ children: [
252
+ /* @__PURE__ */ jsx(Plus, { size: 14 }),
253
+ " Ajouter une entit\xE9"
254
+ ]
255
+ }
256
+ )
257
+ ] })
258
+ ] }),
259
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 pt-1", children: [
260
+ /* @__PURE__ */ jsxs(
261
+ "button",
262
+ {
263
+ type: "submit",
264
+ disabled: saving,
265
+ className: "bg-primary hover:bg-primary-hover flex flex-1 items-center justify-center gap-2 rounded-xl py-2.5 text-sm font-medium text-white transition disabled:opacity-50",
266
+ children: [
267
+ saving && /* @__PURE__ */ jsx(Loader2, { size: 16, className: "animate-spin" }),
268
+ editTarget ? "Enregistrer" : "Cr\xE9er"
269
+ ]
270
+ }
271
+ ),
272
+ /* @__PURE__ */ jsx(
273
+ "button",
274
+ {
275
+ type: "button",
276
+ onClick: () => setShowForm(false),
277
+ className: "bg-bg text-text hover:bg-border-light flex-1 rounded-xl py-2.5 text-sm font-medium transition",
278
+ children: "Annuler"
279
+ }
280
+ )
281
+ ] })
282
+ ] })
283
+ ] }) }),
284
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-6", children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-3xl", children: loading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-20", children: /* @__PURE__ */ jsx(Loader2, { size: 32, className: "text-primary animate-spin" }) }) : types.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "text-text-muted py-20 text-center", children: [
285
+ /* @__PURE__ */ jsx(Tag, { size: 40, className: "mx-auto mb-3 opacity-30" }),
286
+ /* @__PURE__ */ jsx("p", { children: "Aucun type de workspace cr\xE9\xE9." })
287
+ ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-2", children: types.map((t) => {
288
+ const displayEntities = getDisplayEntities(t);
289
+ return /* @__PURE__ */ jsxs(
290
+ "div",
291
+ {
292
+ className: "bg-surface border-border group overflow-hidden rounded-xl border transition hover:shadow-md",
293
+ children: [
294
+ /* @__PURE__ */ jsxs(
295
+ "div",
296
+ {
297
+ className: "flex cursor-pointer items-center justify-between px-5 py-4",
298
+ onClick: () => setSelectedId(selectedId === t._id ? null : t._id),
299
+ children: [
300
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
301
+ /* @__PURE__ */ jsx("span", { className: "bg-primary h-2 w-2 rounded-full" }),
302
+ /* @__PURE__ */ jsx("span", { className: "text-text font-medium", children: t.name }),
303
+ displayEntities.length > 0 && /* @__PURE__ */ jsxs("span", { className: "text-text-muted text-xs", children: [
304
+ displayEntities.length,
305
+ " entit\xE9",
306
+ displayEntities.length !== 1 ? "s" : ""
307
+ ] })
308
+ ] }),
309
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 opacity-0 transition group-hover:opacity-100", children: [
310
+ /* @__PURE__ */ jsx(
311
+ "button",
312
+ {
313
+ type: "button",
314
+ onClick: (e) => {
315
+ e.stopPropagation();
316
+ openEdit(t);
317
+ },
318
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-2 transition",
319
+ title: "Modifier",
320
+ children: /* @__PURE__ */ jsx(Pencil, { size: 16 })
321
+ }
322
+ ),
323
+ /* @__PURE__ */ jsx(
324
+ "button",
325
+ {
326
+ type: "button",
327
+ onClick: (e) => {
328
+ e.stopPropagation();
329
+ confirmDelete(t._id);
330
+ },
331
+ className: "text-text-muted hover:text-danger hover:bg-danger-light rounded-lg p-2 transition",
332
+ title: "Supprimer",
333
+ children: /* @__PURE__ */ jsx(Trash2, { size: 16 })
334
+ }
335
+ )
336
+ ] })
337
+ ]
338
+ }
339
+ ),
340
+ selectedId === t._id && /* @__PURE__ */ jsxs("div", { className: "border-border-light space-y-2 border-t px-5 pt-0 pb-4", children: [
341
+ displayEntities.length > 0 && /* @__PURE__ */ jsxs("div", { className: "pt-2", children: [
342
+ /* @__PURE__ */ jsx("p", { className: "text-text-muted mb-1.5 text-xs", children: "Entit\xE9s" }),
343
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: displayEntities.map((ent) => /* @__PURE__ */ jsxs(
344
+ "span",
345
+ {
346
+ className: "bg-bg text-text-secondary inline-flex items-center gap-1.5 rounded-lg px-2 py-1 text-xs",
347
+ children: [
348
+ ent.isSecret && /* @__PURE__ */ jsx(
349
+ Lock,
350
+ {
351
+ size: 10,
352
+ className: "text-warning shrink-0"
353
+ }
354
+ ),
355
+ /* @__PURE__ */ jsxs("span", { className: "text-text font-medium", children: [
356
+ ent.key,
357
+ ":"
358
+ ] }),
359
+ /* @__PURE__ */ jsx("span", { children: ent.isSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022" : ent.value })
360
+ ]
361
+ },
362
+ ent.key
363
+ )) })
364
+ ] }),
365
+ /* @__PURE__ */ jsxs("div", { className: "text-text-muted flex items-center gap-2 pt-1 text-xs", children: [
366
+ /* @__PURE__ */ jsx(Clock, { size: 12 }),
367
+ /* @__PURE__ */ jsxs("span", { children: [
368
+ "Cr\xE9\xE9 le",
369
+ " ",
370
+ format(
371
+ new Date(t.createdAt),
372
+ "dd MMM yyyy \xE0 HH:mm",
373
+ { locale: fr }
374
+ )
375
+ ] })
376
+ ] }),
377
+ t.createdBy && /* @__PURE__ */ jsxs("div", { className: "text-text-muted flex items-center gap-2 text-xs", children: [
378
+ /* @__PURE__ */ jsx(User, { size: 12 }),
379
+ /* @__PURE__ */ jsxs("span", { children: [
380
+ "Cr\xE9\xE9 par ",
381
+ t.createdBy
382
+ ] })
383
+ ] })
384
+ ] })
385
+ ]
386
+ },
387
+ t._id
388
+ );
389
+ }) }) }) })
390
+ ] });
391
+ }
392
+
393
+ // src/admin/WorkspaceAgentsPage.tsx
394
+ import { useEffect as useEffect2, useState as useState2, useCallback as useCallback2, useId } from "react";
395
+ import {
396
+ Plus as Plus2,
397
+ Pencil as Pencil2,
398
+ Trash2 as Trash22,
399
+ Loader2 as Loader22,
400
+ X as X2,
401
+ Bot,
402
+ Check,
403
+ Search,
404
+ ArrowLeft as ArrowLeft2,
405
+ Webhook,
406
+ AlertTriangle,
407
+ CircleCheck,
408
+ CircleX,
409
+ ExternalLink
410
+ } from "lucide-react";
411
+ import { formatDistanceToNow } from "date-fns";
412
+ import { fr as fr2 } from "date-fns/locale";
413
+
414
+ // src/admin/constants.ts
415
+ var AVAILABLE_WEBHOOK_EVENTS = [
416
+ "*",
417
+ "workspace.created",
418
+ "workspace.updated",
419
+ "workspace.deleted",
420
+ "workspace.statusChanged",
421
+ "workspace.member.added",
422
+ "workspace.member.updated",
423
+ "workspace.member.removed",
424
+ "workspace.message.created",
425
+ "workspace.message.updated",
426
+ "workspace.message.deleted",
427
+ "workspace.member.user.mentioned",
428
+ "workspace.member.agent.mentioned",
429
+ "workspace.member.user.message.replied",
430
+ "workspace.member.agent.message.replied",
431
+ "workspace.message.reaction.created",
432
+ "workspace.message.reaction.deleted",
433
+ "private.workspace.created",
434
+ "private.workspace.message.created",
435
+ "private.workspace.member.added"
436
+ ];
437
+
438
+ // src/admin/WorkspaceAgentsPage.tsx
439
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
440
+ function WebhookDialog({ agent, open, onClose }) {
441
+ const { webhookApi } = useWorkspaceApi();
442
+ const webhookDialogTitleId = useId();
443
+ const [webhooks, setWebhooks] = useState2([]);
444
+ const [loading, setLoading] = useState2(true);
445
+ const [error, setError] = useState2("");
446
+ const [showForm, setShowForm] = useState2(false);
447
+ const [editingId, setEditingId] = useState2(null);
448
+ const [saving, setSaving] = useState2(false);
449
+ const [deleteId, setDeleteId] = useState2(null);
450
+ const [form, setForm] = useState2({
451
+ name: "",
452
+ targetUrl: "",
453
+ secretKey: "x-api-key",
454
+ secretValue: "",
455
+ eventTypes: ["*"],
456
+ active: true
457
+ });
458
+ const loadWebhooks = useCallback2(async () => {
459
+ try {
460
+ setLoading(true);
461
+ const data = await webhookApi.list({ agentId: agent.agentId });
462
+ setWebhooks(data);
463
+ } catch (e) {
464
+ setError(e.message);
465
+ } finally {
466
+ setLoading(false);
467
+ }
468
+ }, [agent.agentId, webhookApi]);
469
+ useEffect2(() => {
470
+ if (open) loadWebhooks();
471
+ }, [open, loadWebhooks]);
472
+ const resetForm = () => {
473
+ setForm({
474
+ name: "",
475
+ targetUrl: "",
476
+ secretKey: "x-api-key",
477
+ secretValue: "",
478
+ eventTypes: ["*"],
479
+ active: true
480
+ });
481
+ setEditingId(null);
482
+ setShowForm(false);
483
+ };
484
+ const openCreateForm = () => {
485
+ resetForm();
486
+ setShowForm(true);
487
+ };
488
+ const openEditForm = (sub) => {
489
+ setForm({
490
+ name: sub.name,
491
+ targetUrl: sub.targetUrl,
492
+ secretKey: sub.secretKey,
493
+ secretValue: "",
494
+ eventTypes: [...sub.eventTypes],
495
+ active: sub.active
496
+ });
497
+ setEditingId(sub._id);
498
+ setShowForm(true);
499
+ };
500
+ const handleSubmit = async (e) => {
501
+ e.preventDefault();
502
+ if (!form.name.trim() || !form.targetUrl.trim()) return;
503
+ try {
504
+ setSaving(true);
505
+ if (editingId) {
506
+ await webhookApi.update(editingId, {
507
+ name: form.name,
508
+ targetUrl: form.targetUrl,
509
+ secretKey: form.secretKey,
510
+ eventTypes: form.eventTypes,
511
+ active: form.active,
512
+ isAgent: true,
513
+ agentId: agent.agentId,
514
+ ...form.secretValue.trim() ? { secretValue: form.secretValue } : {}
515
+ });
516
+ } else {
517
+ await webhookApi.create({
518
+ name: form.name,
519
+ targetUrl: form.targetUrl,
520
+ secretKey: form.secretKey,
521
+ secretValue: form.secretValue,
522
+ eventTypes: form.eventTypes,
523
+ active: form.active,
524
+ isAgent: true,
525
+ agentId: agent.agentId
526
+ });
527
+ }
528
+ resetForm();
529
+ await loadWebhooks();
530
+ } catch (e2) {
531
+ setError(e2.message);
532
+ } finally {
533
+ setSaving(false);
534
+ }
535
+ };
536
+ const toggleActive = async (sub) => {
537
+ try {
538
+ await webhookApi.update(sub._id, { active: !sub.active });
539
+ setWebhooks(
540
+ (prev) => prev.map((s) => s._id === sub._id ? { ...s, active: !s.active } : s)
541
+ );
542
+ } catch (e) {
543
+ setError(e.message);
544
+ }
545
+ };
546
+ const executeDelete = async () => {
547
+ if (!deleteId) return;
548
+ try {
549
+ await webhookApi.delete(deleteId);
550
+ setWebhooks((prev) => prev.filter((s) => s._id !== deleteId));
551
+ } catch (e) {
552
+ setError(e.message);
553
+ }
554
+ };
555
+ const toggleEvent = (event) => {
556
+ setForm((prev) => {
557
+ if (event === "*") {
558
+ return { ...prev, eventTypes: ["*"] };
559
+ }
560
+ const without = prev.eventTypes.filter((e) => e !== "*" && e !== event);
561
+ if (prev.eventTypes.includes(event)) {
562
+ return { ...prev, eventTypes: without.length ? without : ["*"] };
563
+ }
564
+ return { ...prev, eventTypes: [...without, event] };
565
+ });
566
+ };
567
+ return /* @__PURE__ */ jsx2(
568
+ Modal,
569
+ {
570
+ open,
571
+ onClose,
572
+ zIndexClass: "z-[100]",
573
+ backdropClassName: "bg-black/40 backdrop-blur-sm",
574
+ size: "2xl",
575
+ panelClassName: "border-border bg-surface flex max-h-[85vh] w-full flex-col overflow-hidden rounded-2xl border shadow-xl",
576
+ className: "p-4",
577
+ closeOnBackdropClick: true,
578
+ closeOnEscape: !saving,
579
+ headingId: webhookDialogTitleId,
580
+ ariaLabelledBy: webhookDialogTitleId,
581
+ title: `Webhooks \u2014 ${agent.agentName}`,
582
+ description: /* @__PURE__ */ jsx2("p", { className: "text-text-muted mt-1 font-mono text-xs", children: agent.agentId }),
583
+ titleClassName: "text-base font-semibold",
584
+ titleIcon: /* @__PURE__ */ jsx2("div", { className: "bg-primary-light text-primary flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ jsx2(Webhook, { size: 18 }) }),
585
+ headerTrailing: /* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-2", children: [
586
+ /* @__PURE__ */ jsxs2(
587
+ "button",
588
+ {
589
+ type: "button",
590
+ onClick: openCreateForm,
591
+ className: "bg-primary hover:bg-primary-hover flex items-center gap-1.5 rounded-xl px-3 py-2 text-xs font-medium text-white transition",
592
+ children: [
593
+ /* @__PURE__ */ jsx2(Plus2, { size: 14 }),
594
+ " Nouveau"
595
+ ]
596
+ }
597
+ ),
598
+ /* @__PURE__ */ jsx2(
599
+ "button",
600
+ {
601
+ type: "button",
602
+ onClick: onClose,
603
+ className: "text-text-muted hover:text-text transition",
604
+ "aria-label": "Fermer",
605
+ children: /* @__PURE__ */ jsx2(X2, { size: 20 })
606
+ }
607
+ )
608
+ ] }),
609
+ headerClassName: "items-center",
610
+ children: /* @__PURE__ */ jsxs2("div", { className: "min-h-0 flex-1 overflow-y-auto p-6", children: [
611
+ error && /* @__PURE__ */ jsxs2("div", { className: "border-danger/30 bg-danger-light text-danger mb-4 rounded-xl border px-4 py-3 text-sm", children: [
612
+ error,
613
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setError(""), className: "ml-2 underline", children: "\xD7" })
614
+ ] }),
615
+ /* @__PURE__ */ jsx2(
616
+ ConfirmModal,
617
+ {
618
+ open: !!deleteId,
619
+ onClose: () => setDeleteId(null),
620
+ onConfirm: executeDelete,
621
+ title: "Supprimer ce webhook",
622
+ description: "Voulez-vous vraiment supprimer ce webhook ?",
623
+ danger: true
624
+ }
625
+ ),
626
+ showForm && /* @__PURE__ */ jsxs2("div", { className: "bg-bg border-border mb-4 rounded-xl border p-5", children: [
627
+ /* @__PURE__ */ jsxs2("div", { className: "mb-3 flex items-center justify-between", children: [
628
+ /* @__PURE__ */ jsx2("h3", { className: "text-text text-sm font-semibold", children: editingId ? "Modifier le webhook" : "Nouveau webhook" }),
629
+ /* @__PURE__ */ jsx2(
630
+ "button",
631
+ {
632
+ type: "button",
633
+ onClick: resetForm,
634
+ className: "text-text-muted hover:text-text transition",
635
+ children: /* @__PURE__ */ jsx2(X2, { size: 16 })
636
+ }
637
+ )
638
+ ] }),
639
+ /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "space-y-3", children: [
640
+ /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-2 gap-3", children: [
641
+ /* @__PURE__ */ jsxs2("div", { children: [
642
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1 block text-xs", children: "Nom" }),
643
+ /* @__PURE__ */ jsx2(
644
+ "input",
645
+ {
646
+ value: form.name,
647
+ onChange: (e) => setForm({ ...form, name: e.target.value }),
648
+ required: true,
649
+ placeholder: "Mon webhook agent",
650
+ className: "bg-surface border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
651
+ }
652
+ )
653
+ ] }),
654
+ /* @__PURE__ */ jsxs2("div", { children: [
655
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1 block text-xs", children: "URL cible" }),
656
+ /* @__PURE__ */ jsx2(
657
+ "input",
658
+ {
659
+ value: form.targetUrl,
660
+ onChange: (e) => setForm({ ...form, targetUrl: e.target.value }),
661
+ required: true,
662
+ placeholder: "https://api.example.com/webhooks",
663
+ className: "bg-surface border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
664
+ }
665
+ )
666
+ ] })
667
+ ] }),
668
+ /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-2 gap-3", children: [
669
+ /* @__PURE__ */ jsxs2("div", { children: [
670
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1 block text-xs", children: "Cl\xE9 du header" }),
671
+ /* @__PURE__ */ jsx2(
672
+ "input",
673
+ {
674
+ value: form.secretKey,
675
+ onChange: (e) => setForm({ ...form, secretKey: e.target.value }),
676
+ required: true,
677
+ placeholder: "x-api-key",
678
+ className: "bg-surface border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
679
+ }
680
+ )
681
+ ] }),
682
+ /* @__PURE__ */ jsxs2("div", { children: [
683
+ /* @__PURE__ */ jsxs2("label", { className: "text-text-secondary mb-1 block text-xs", children: [
684
+ "Secret",
685
+ " ",
686
+ editingId && /* @__PURE__ */ jsx2("span", { className: "text-text-muted", children: "(vide = inchang\xE9)" })
687
+ ] }),
688
+ /* @__PURE__ */ jsx2(
689
+ "input",
690
+ {
691
+ value: form.secretValue,
692
+ onChange: (e) => setForm({ ...form, secretValue: e.target.value }),
693
+ required: !editingId,
694
+ type: "password",
695
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
696
+ className: "bg-surface border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
697
+ }
698
+ )
699
+ ] })
700
+ ] }),
701
+ /* @__PURE__ */ jsxs2("div", { children: [
702
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1.5 block text-xs", children: "\xC9v\xE9nements" }),
703
+ /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-1.5", children: AVAILABLE_WEBHOOK_EVENTS.map((evt) => /* @__PURE__ */ jsx2(
704
+ "button",
705
+ {
706
+ type: "button",
707
+ onClick: () => toggleEvent(evt),
708
+ className: `rounded-full px-2.5 py-1 text-xs font-medium transition ${form.eventTypes.includes(evt) ? "bg-primary text-white" : "bg-surface border-border text-text-secondary hover:text-text hover:bg-border-light border"}`,
709
+ children: evt === "*" ? "Tous (*)" : evt
710
+ },
711
+ evt
712
+ )) })
713
+ ] }),
714
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3", children: [
715
+ /* @__PURE__ */ jsx2(
716
+ "button",
717
+ {
718
+ type: "button",
719
+ onClick: () => setForm({ ...form, active: !form.active }),
720
+ className: `relative h-5 w-10 rounded-full transition ${form.active ? "bg-primary" : "bg-border"}`,
721
+ children: /* @__PURE__ */ jsx2(
722
+ "span",
723
+ {
724
+ className: `absolute top-0.5 left-0.5 h-4 w-4 rounded-full bg-white shadow transition-transform ${form.active ? "translate-x-5" : ""}`
725
+ }
726
+ )
727
+ }
728
+ ),
729
+ /* @__PURE__ */ jsx2("span", { className: "text-text text-sm", children: form.active ? "Active" : "Inactive" })
730
+ ] }),
731
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-2 pt-1", children: [
732
+ /* @__PURE__ */ jsxs2(
733
+ "button",
734
+ {
735
+ type: "submit",
736
+ disabled: saving,
737
+ className: "bg-primary hover:bg-primary-hover flex items-center gap-1.5 rounded-xl px-4 py-2 text-sm font-medium text-white transition disabled:opacity-50",
738
+ children: [
739
+ saving && /* @__PURE__ */ jsx2(Loader22, { size: 14, className: "animate-spin" }),
740
+ editingId ? "Enregistrer" : "Cr\xE9er"
741
+ ]
742
+ }
743
+ ),
744
+ /* @__PURE__ */ jsx2(
745
+ "button",
746
+ {
747
+ type: "button",
748
+ onClick: resetForm,
749
+ className: "bg-bg text-text hover:bg-border-light rounded-xl px-4 py-2 text-sm font-medium transition",
750
+ children: "Annuler"
751
+ }
752
+ )
753
+ ] })
754
+ ] })
755
+ ] }),
756
+ loading ? /* @__PURE__ */ jsx2("div", { className: "flex justify-center py-10", children: /* @__PURE__ */ jsx2(Loader22, { size: 20, className: "text-primary animate-spin" }) }) : webhooks.length === 0 ? /* @__PURE__ */ jsxs2("div", { className: "text-text-muted py-12 text-center", children: [
757
+ /* @__PURE__ */ jsx2(Webhook, { size: 32, className: "mx-auto mb-2 opacity-30" }),
758
+ /* @__PURE__ */ jsx2("p", { className: "text-sm", children: "Aucun webhook branch\xE9 \xE0 cet agent." }),
759
+ /* @__PURE__ */ jsx2("p", { className: "text-text-muted mt-1 text-xs", children: "Cr\xE9ez un webhook pour que l'agent puisse recevoir les \xE9v\xE9nements." })
760
+ ] }) : /* @__PURE__ */ jsx2("div", { className: "space-y-2", children: webhooks.map((sub) => /* @__PURE__ */ jsx2(
761
+ "div",
762
+ {
763
+ className: "bg-bg border-border group rounded-xl border px-4 py-3 transition hover:shadow-sm",
764
+ children: /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-3", children: [
765
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
766
+ /* @__PURE__ */ jsxs2("div", { className: "mb-1 flex items-center gap-2", children: [
767
+ /* @__PURE__ */ jsx2("h4", { className: "text-text truncate text-sm font-medium", children: sub.name }),
768
+ sub.active ? /* @__PURE__ */ jsxs2("span", { className: "bg-success-light text-success ring-success/30 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs ring-1", children: [
769
+ /* @__PURE__ */ jsx2(CircleCheck, { size: 10 }),
770
+ "Active"
771
+ ] }) : /* @__PURE__ */ jsxs2("span", { className: "text-text-muted bg-bg border-border inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs", children: [
772
+ /* @__PURE__ */ jsx2(CircleX, { size: 10 }),
773
+ "Inactive"
774
+ ] })
775
+ ] }),
776
+ /* @__PURE__ */ jsxs2("div", { className: "text-text-muted mb-1.5 flex items-center gap-1 text-xs", children: [
777
+ /* @__PURE__ */ jsx2(ExternalLink, { size: 10, className: "shrink-0" }),
778
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: sub.targetUrl })
779
+ ] }),
780
+ /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-1", children: sub.eventTypes.map((evt) => /* @__PURE__ */ jsx2(
781
+ "span",
782
+ {
783
+ className: "bg-surface text-text-secondary rounded px-1.5 py-0.5 text-xs",
784
+ children: evt === "*" ? "Tous" : evt
785
+ },
786
+ evt
787
+ )) })
788
+ ] }),
789
+ /* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-1", children: [
790
+ /* @__PURE__ */ jsx2(
791
+ "button",
792
+ {
793
+ type: "button",
794
+ onClick: () => toggleActive(sub),
795
+ className: `rounded-lg p-1.5 transition ${sub.active ? "text-success hover:bg-success-light" : "text-text-muted hover:bg-bg"}`,
796
+ title: sub.active ? "D\xE9sactiver" : "Activer",
797
+ children: sub.active ? /* @__PURE__ */ jsx2(CircleCheck, { size: 14 }) : /* @__PURE__ */ jsx2(CircleX, { size: 14 })
798
+ }
799
+ ),
800
+ /* @__PURE__ */ jsx2(
801
+ "button",
802
+ {
803
+ type: "button",
804
+ onClick: () => openEditForm(sub),
805
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-1.5 transition",
806
+ title: "Modifier",
807
+ children: /* @__PURE__ */ jsx2(Pencil2, { size: 14 })
808
+ }
809
+ ),
810
+ /* @__PURE__ */ jsx2(
811
+ "button",
812
+ {
813
+ type: "button",
814
+ onClick: () => setDeleteId(sub._id),
815
+ className: "text-text-muted hover:text-danger hover:bg-danger-light rounded-lg p-1.5 opacity-0 transition group-hover:opacity-100",
816
+ title: "Supprimer",
817
+ children: /* @__PURE__ */ jsx2(Trash22, { size: 14 })
818
+ }
819
+ )
820
+ ] })
821
+ ] })
822
+ },
823
+ sub._id
824
+ )) })
825
+ ] })
826
+ }
827
+ );
828
+ }
829
+ function WorkspaceAgentsPage({ onBack }) {
830
+ const { agentApi, webhookApi } = useWorkspaceApi();
831
+ const [agents, setAgents] = useState2([]);
832
+ const [loading, setLoading] = useState2(true);
833
+ const [error, setError] = useState2("");
834
+ const [search, setSearch] = useState2("");
835
+ const [showForm, setShowForm] = useState2(false);
836
+ const [editingAgent, setEditingAgent] = useState2(null);
837
+ const [saving, setSaving] = useState2(false);
838
+ const [form, setForm] = useState2({ agentId: "", agentName: "" });
839
+ const [webhookForm, setWebhookForm] = useState2({
840
+ name: "",
841
+ targetUrl: "",
842
+ secretKey: "x-api-key",
843
+ secretValue: "",
844
+ eventTypes: ["*"],
845
+ active: true
846
+ });
847
+ const [webhookNameTouched, setWebhookNameTouched] = useState2(false);
848
+ const [deleteAgent, setDeleteAgent] = useState2(null);
849
+ const [deleteInfo, setDeleteInfo] = useState2(null);
850
+ const [loadingDeleteInfo, setLoadingDeleteInfo] = useState2(false);
851
+ const [webhookDialogAgent, setWebhookDialogAgent] = useState2(null);
852
+ const load = useCallback2(async () => {
853
+ try {
854
+ setLoading(true);
855
+ const data = await agentApi.list(search.trim() || void 0);
856
+ setAgents(data);
857
+ } catch (e) {
858
+ setError(e.message);
859
+ } finally {
860
+ setLoading(false);
861
+ }
862
+ }, [search, agentApi]);
863
+ useEffect2(() => {
864
+ const t = setTimeout(() => load(), 300);
865
+ return () => clearTimeout(t);
866
+ }, [load]);
867
+ const resetForm = () => {
868
+ setForm({ agentId: "", agentName: "" });
869
+ setWebhookForm({
870
+ name: "",
871
+ targetUrl: "",
872
+ secretKey: "x-api-key",
873
+ secretValue: "",
874
+ eventTypes: ["*"],
875
+ active: true
876
+ });
877
+ setWebhookNameTouched(false);
878
+ setEditingAgent(null);
879
+ setShowForm(false);
880
+ };
881
+ const openCreate = () => {
882
+ setForm({ agentId: "", agentName: "" });
883
+ setEditingAgent(null);
884
+ setShowForm(true);
885
+ };
886
+ const openEdit = (agent) => {
887
+ setForm({ agentId: agent.agentId, agentName: agent.agentName });
888
+ setEditingAgent(agent);
889
+ setShowForm(true);
890
+ };
891
+ const toggleCreateEvent = (event) => {
892
+ setWebhookForm((prev) => {
893
+ if (event === "*") {
894
+ return { ...prev, eventTypes: ["*"] };
895
+ }
896
+ const without = prev.eventTypes.filter((e) => e !== "*" && e !== event);
897
+ if (prev.eventTypes.includes(event)) {
898
+ return { ...prev, eventTypes: without.length ? without : ["*"] };
899
+ }
900
+ return { ...prev, eventTypes: [...without, event] };
901
+ });
902
+ };
903
+ const handleSubmit = async (e) => {
904
+ e.preventDefault();
905
+ if (!form.agentName.trim()) return;
906
+ if (!editingAgent && !form.agentId.trim()) return;
907
+ if (!editingAgent && (!webhookForm.name.trim() || !webhookForm.targetUrl.trim() || !webhookForm.secretValue.trim()))
908
+ return;
909
+ try {
910
+ setSaving(true);
911
+ if (editingAgent) {
912
+ await agentApi.update(editingAgent._id, {
913
+ agentName: form.agentName
914
+ });
915
+ resetForm();
916
+ await load();
917
+ } else {
918
+ const created = await agentApi.create({
919
+ agentId: form.agentId.trim(),
920
+ agentName: form.agentName.trim()
921
+ });
922
+ await webhookApi.create({
923
+ name: webhookForm.name.trim(),
924
+ targetUrl: webhookForm.targetUrl.trim(),
925
+ secretKey: webhookForm.secretKey.trim(),
926
+ secretValue: webhookForm.secretValue.trim(),
927
+ eventTypes: webhookForm.eventTypes,
928
+ active: webhookForm.active,
929
+ isAgent: true,
930
+ agentId: created.agentId
931
+ });
932
+ resetForm();
933
+ await load();
934
+ }
935
+ } catch (e2) {
936
+ setError(e2.message);
937
+ } finally {
938
+ setSaving(false);
939
+ }
940
+ };
941
+ const requestDelete = async (agent) => {
942
+ setDeleteAgent(agent);
943
+ setDeleteInfo(null);
944
+ setLoadingDeleteInfo(true);
945
+ try {
946
+ const info = await agentApi.getDeleteInfo(agent._id);
947
+ setDeleteInfo(info);
948
+ } catch {
949
+ setDeleteInfo({ webhookCount: 0, memberCount: 0 });
950
+ } finally {
951
+ setLoadingDeleteInfo(false);
952
+ }
953
+ };
954
+ const handleDelete = async () => {
955
+ if (!deleteAgent) return;
956
+ try {
957
+ await agentApi.delete(deleteAgent._id);
958
+ setAgents((prev) => prev.filter((a) => a._id !== deleteAgent._id));
959
+ } catch (e) {
960
+ setError(e.message);
961
+ }
962
+ };
963
+ const buildDeleteDescription = () => {
964
+ if (loadingDeleteInfo) {
965
+ return /* @__PURE__ */ jsxs2("span", { className: "flex items-center gap-2", children: [
966
+ /* @__PURE__ */ jsx2(Loader22, { size: 14, className: "animate-spin" }),
967
+ "Chargement des informations..."
968
+ ] });
969
+ }
970
+ if (!deleteInfo) return "Voulez-vous vraiment supprimer cet agent ?";
971
+ const parts = [];
972
+ if (deleteInfo.webhookCount > 0) {
973
+ parts.push(
974
+ `${deleteInfo.webhookCount} webhook${deleteInfo.webhookCount > 1 ? "s" : ""}`
975
+ );
976
+ }
977
+ if (deleteInfo.memberCount > 0) {
978
+ parts.push(
979
+ `membre de ${deleteInfo.memberCount} workspace${deleteInfo.memberCount > 1 ? "s" : ""}`
980
+ );
981
+ }
982
+ if (parts.length === 0) {
983
+ return `Voulez-vous vraiment supprimer l'agent "${deleteAgent?.agentName}" ?`;
984
+ }
985
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
986
+ `Voulez-vous vraiment supprimer l'agent "`,
987
+ /* @__PURE__ */ jsx2("strong", { children: deleteAgent?.agentName }),
988
+ '" ? Cela supprimera \xE9galement :',
989
+ " ",
990
+ /* @__PURE__ */ jsx2("span", { className: "text-danger font-medium", children: parts.join(" et ") }),
991
+ "."
992
+ ] });
993
+ };
994
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col", children: [
995
+ /* @__PURE__ */ jsxs2("div", { className: "bg-surface border-border flex shrink-0 items-center gap-4 border-b px-6 py-4", children: [
996
+ onBack && /* @__PURE__ */ jsx2(
997
+ "button",
998
+ {
999
+ type: "button",
1000
+ onClick: () => onBack(),
1001
+ className: "text-text-muted hover:text-text hover:bg-bg rounded-lg p-1.5 transition",
1002
+ children: /* @__PURE__ */ jsx2(ArrowLeft2, { size: 20 })
1003
+ }
1004
+ ),
1005
+ /* @__PURE__ */ jsx2("div", { className: "bg-primary-light text-primary flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ jsx2(Bot, { size: 24 }) }),
1006
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1", children: [
1007
+ /* @__PURE__ */ jsx2("h1", { className: "text-text text-xl font-semibold", children: "Agents Workspace" }),
1008
+ /* @__PURE__ */ jsx2("p", { className: "text-text-secondary text-sm", children: "G\xE9rer les agents disponibles pour les workspaces" })
1009
+ ] }),
1010
+ /* @__PURE__ */ jsxs2(
1011
+ "button",
1012
+ {
1013
+ type: "button",
1014
+ onClick: openCreate,
1015
+ className: "bg-primary hover:bg-primary-hover flex items-center gap-2 rounded-xl px-4 py-2.5 text-sm font-medium text-white transition",
1016
+ children: [
1017
+ /* @__PURE__ */ jsx2(Plus2, { size: 16 }),
1018
+ " Nouvel agent"
1019
+ ]
1020
+ }
1021
+ )
1022
+ ] }),
1023
+ error && /* @__PURE__ */ jsxs2("div", { className: "border-danger/30 bg-danger-light text-danger mx-6 mt-3 rounded-xl border px-4 py-3 text-sm", children: [
1024
+ error,
1025
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setError(""), className: "ml-2 underline", children: "\xD7" })
1026
+ ] }),
1027
+ /* @__PURE__ */ jsx2(
1028
+ ConfirmModal,
1029
+ {
1030
+ open: !!deleteAgent,
1031
+ onClose: () => setDeleteAgent(null),
1032
+ onConfirm: handleDelete,
1033
+ title: "Supprimer cet agent",
1034
+ description: buildDeleteDescription(),
1035
+ danger: true
1036
+ }
1037
+ ),
1038
+ webhookDialogAgent && /* @__PURE__ */ jsx2(
1039
+ WebhookDialog,
1040
+ {
1041
+ agent: webhookDialogAgent,
1042
+ open: true,
1043
+ onClose: () => setWebhookDialogAgent(null)
1044
+ }
1045
+ ),
1046
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 overflow-y-auto p-6", children: /* @__PURE__ */ jsxs2("div", { className: "mx-auto max-w-3xl", children: [
1047
+ showForm && /* @__PURE__ */ jsxs2("div", { className: "bg-surface border-border mb-6 rounded-2xl border p-6 shadow-sm", children: [
1048
+ /* @__PURE__ */ jsxs2("div", { className: "mb-4 flex items-center justify-between", children: [
1049
+ /* @__PURE__ */ jsx2("h2", { className: "text-text text-base font-semibold", children: editingAgent ? "Modifier l'agent" : "Nouvel agent" }),
1050
+ /* @__PURE__ */ jsx2(
1051
+ "button",
1052
+ {
1053
+ type: "button",
1054
+ onClick: resetForm,
1055
+ className: "text-text-muted hover:text-text transition",
1056
+ children: /* @__PURE__ */ jsx2(X2, { size: 20 })
1057
+ }
1058
+ )
1059
+ ] }),
1060
+ /* @__PURE__ */ jsxs2("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
1061
+ /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-2 gap-4", children: [
1062
+ /* @__PURE__ */ jsxs2("div", { children: [
1063
+ /* @__PURE__ */ jsxs2("label", { className: "text-text-secondary mb-1 block text-xs", children: [
1064
+ "Agent ID",
1065
+ " ",
1066
+ editingAgent && /* @__PURE__ */ jsx2("span", { className: "text-text-muted", children: "(non modifiable)" })
1067
+ ] }),
1068
+ /* @__PURE__ */ jsx2(
1069
+ "input",
1070
+ {
1071
+ value: form.agentId,
1072
+ onChange: (e) => setForm({ ...form, agentId: e.target.value }),
1073
+ required: !editingAgent,
1074
+ disabled: !!editingAgent,
1075
+ placeholder: "ex: james-agent",
1076
+ pattern: "^[a-z0-9-]+$",
1077
+ title: "Uniquement lettres minuscules, chiffres et tirets",
1078
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
1079
+ }
1080
+ )
1081
+ ] }),
1082
+ /* @__PURE__ */ jsxs2("div", { children: [
1083
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1 block text-xs", children: "Nom d'affichage" }),
1084
+ /* @__PURE__ */ jsx2(
1085
+ "input",
1086
+ {
1087
+ value: form.agentName,
1088
+ onChange: (e) => {
1089
+ const newName = e.target.value;
1090
+ setForm({ ...form, agentName: newName });
1091
+ if (!editingAgent && !webhookNameTouched) {
1092
+ setWebhookForm((prev) => ({
1093
+ ...prev,
1094
+ name: newName.trim() ? `Webhook ${newName.trim()}` : ""
1095
+ }));
1096
+ }
1097
+ },
1098
+ required: true,
1099
+ autoFocus: !!editingAgent,
1100
+ placeholder: "ex: James",
1101
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1102
+ }
1103
+ )
1104
+ ] })
1105
+ ] }),
1106
+ editingAgent && /* @__PURE__ */ jsx2("p", { className: "text-warning text-xs", children: "\u26A0 La modification du nom sera propag\xE9e \xE0 tous les membres de workspace ayant cet agent." }),
1107
+ !editingAgent && /* @__PURE__ */ jsx2(Fragment, { children: /* @__PURE__ */ jsxs2("div", { className: "border-border mt-2 border-t pt-4", children: [
1108
+ /* @__PURE__ */ jsxs2("div", { className: "mb-3 flex items-center gap-2", children: [
1109
+ /* @__PURE__ */ jsx2("div", { className: "bg-primary-light text-primary flex h-7 w-7 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ jsx2(Webhook, { size: 14 }) }),
1110
+ /* @__PURE__ */ jsx2("h3", { className: "text-text text-sm font-semibold", children: "Webhook de l'agent" })
1111
+ ] }),
1112
+ /* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-2 gap-4", children: [
1113
+ /* @__PURE__ */ jsxs2("div", { children: [
1114
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1 block text-xs", children: "Nom du webhook" }),
1115
+ /* @__PURE__ */ jsx2(
1116
+ "input",
1117
+ {
1118
+ value: webhookForm.name,
1119
+ onChange: (e) => {
1120
+ setWebhookNameTouched(true);
1121
+ setWebhookForm({
1122
+ ...webhookForm,
1123
+ name: e.target.value
1124
+ });
1125
+ },
1126
+ required: true,
1127
+ placeholder: "ex: Webhook James",
1128
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1129
+ }
1130
+ )
1131
+ ] }),
1132
+ /* @__PURE__ */ jsxs2("div", { children: [
1133
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1 block text-xs", children: "URL cible" }),
1134
+ /* @__PURE__ */ jsx2(
1135
+ "input",
1136
+ {
1137
+ value: webhookForm.targetUrl,
1138
+ onChange: (e) => setWebhookForm({
1139
+ ...webhookForm,
1140
+ targetUrl: e.target.value
1141
+ }),
1142
+ required: true,
1143
+ placeholder: "https://api.example.com/webhooks",
1144
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1145
+ }
1146
+ )
1147
+ ] })
1148
+ ] }),
1149
+ /* @__PURE__ */ jsxs2("div", { className: "mt-3 grid grid-cols-2 gap-4", children: [
1150
+ /* @__PURE__ */ jsxs2("div", { children: [
1151
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1 block text-xs", children: "Cl\xE9 du header" }),
1152
+ /* @__PURE__ */ jsx2(
1153
+ "input",
1154
+ {
1155
+ value: webhookForm.secretKey,
1156
+ onChange: (e) => setWebhookForm({
1157
+ ...webhookForm,
1158
+ secretKey: e.target.value
1159
+ }),
1160
+ required: true,
1161
+ placeholder: "x-api-key",
1162
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1163
+ }
1164
+ )
1165
+ ] }),
1166
+ /* @__PURE__ */ jsxs2("div", { children: [
1167
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1 block text-xs", children: "Secret" }),
1168
+ /* @__PURE__ */ jsx2(
1169
+ "input",
1170
+ {
1171
+ value: webhookForm.secretValue,
1172
+ onChange: (e) => setWebhookForm({
1173
+ ...webhookForm,
1174
+ secretValue: e.target.value
1175
+ }),
1176
+ required: true,
1177
+ type: "password",
1178
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
1179
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1180
+ }
1181
+ )
1182
+ ] })
1183
+ ] }),
1184
+ /* @__PURE__ */ jsxs2("div", { className: "mt-3", children: [
1185
+ /* @__PURE__ */ jsx2("label", { className: "text-text-secondary mb-1.5 block text-xs", children: "\xC9v\xE9nements" }),
1186
+ /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-1.5", children: AVAILABLE_WEBHOOK_EVENTS.map((evt) => /* @__PURE__ */ jsx2(
1187
+ "button",
1188
+ {
1189
+ type: "button",
1190
+ onClick: () => toggleCreateEvent(evt),
1191
+ className: `rounded-full px-2.5 py-1 text-xs font-medium transition ${webhookForm.eventTypes.includes(evt) ? "bg-primary text-white" : "bg-bg border-border text-text-secondary hover:text-text hover:bg-border-light border"}`,
1192
+ children: evt === "*" ? "Tous (*)" : evt
1193
+ },
1194
+ evt
1195
+ )) })
1196
+ ] }),
1197
+ /* @__PURE__ */ jsxs2("div", { className: "mt-3 flex items-center gap-3", children: [
1198
+ /* @__PURE__ */ jsx2(
1199
+ "button",
1200
+ {
1201
+ type: "button",
1202
+ onClick: () => setWebhookForm({
1203
+ ...webhookForm,
1204
+ active: !webhookForm.active
1205
+ }),
1206
+ className: `relative h-5 w-10 rounded-full transition ${webhookForm.active ? "bg-primary" : "bg-border"}`,
1207
+ children: /* @__PURE__ */ jsx2(
1208
+ "span",
1209
+ {
1210
+ className: `absolute top-0.5 left-0.5 h-4 w-4 rounded-full bg-white shadow transition-transform ${webhookForm.active ? "translate-x-5" : ""}`
1211
+ }
1212
+ )
1213
+ }
1214
+ ),
1215
+ /* @__PURE__ */ jsx2("span", { className: "text-text text-sm", children: webhookForm.active ? "Webhook actif" : "Webhook inactif" })
1216
+ ] })
1217
+ ] }) }),
1218
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-3 pt-1", children: [
1219
+ /* @__PURE__ */ jsxs2(
1220
+ "button",
1221
+ {
1222
+ type: "submit",
1223
+ disabled: saving,
1224
+ className: "bg-primary hover:bg-primary-hover flex items-center gap-2 rounded-xl px-5 py-2 text-sm font-medium text-white transition disabled:opacity-50",
1225
+ children: [
1226
+ saving ? /* @__PURE__ */ jsx2(Loader22, { size: 16, className: "animate-spin" }) : /* @__PURE__ */ jsx2(Check, { size: 16 }),
1227
+ editingAgent ? "Enregistrer" : "Cr\xE9er l'agent + webhook"
1228
+ ]
1229
+ }
1230
+ ),
1231
+ /* @__PURE__ */ jsx2(
1232
+ "button",
1233
+ {
1234
+ type: "button",
1235
+ onClick: resetForm,
1236
+ className: "bg-bg text-text hover:bg-border-light rounded-xl px-5 py-2 text-sm font-medium transition",
1237
+ children: "Annuler"
1238
+ }
1239
+ )
1240
+ ] })
1241
+ ] })
1242
+ ] }),
1243
+ /* @__PURE__ */ jsxs2("div", { className: "relative mb-4", children: [
1244
+ /* @__PURE__ */ jsx2(
1245
+ Search,
1246
+ {
1247
+ size: 16,
1248
+ className: "text-text-muted absolute top-1/2 left-3 -translate-y-1/2"
1249
+ }
1250
+ ),
1251
+ /* @__PURE__ */ jsx2(
1252
+ "input",
1253
+ {
1254
+ value: search,
1255
+ onChange: (e) => setSearch(e.target.value),
1256
+ placeholder: "Rechercher un agent...",
1257
+ className: "bg-surface border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full max-w-md rounded-xl border py-2 pr-4 pl-10 text-sm focus:ring-2 focus:outline-none"
1258
+ }
1259
+ )
1260
+ ] }),
1261
+ loading ? /* @__PURE__ */ jsx2("div", { className: "flex justify-center py-10", children: /* @__PURE__ */ jsx2(Loader22, { size: 24, className: "text-primary animate-spin" }) }) : agents.length === 0 ? /* @__PURE__ */ jsxs2("div", { className: "text-text-muted py-20 text-center", children: [
1262
+ /* @__PURE__ */ jsx2(Bot, { size: 40, className: "mx-auto mb-3 opacity-30" }),
1263
+ /* @__PURE__ */ jsx2("p", { className: "text-sm", children: "Aucun agent trouv\xE9." }),
1264
+ /* @__PURE__ */ jsx2("p", { className: "text-text-muted mt-1 text-xs", children: "Cr\xE9ez un agent pour le rendre disponible dans les workspaces." })
1265
+ ] }) : /* @__PURE__ */ jsx2("div", { className: "space-y-2", children: agents.map((agent) => /* @__PURE__ */ jsxs2(
1266
+ "div",
1267
+ {
1268
+ className: "bg-surface border-border group flex items-center gap-4 rounded-xl border px-5 py-4 transition hover:shadow-md",
1269
+ children: [
1270
+ /* @__PURE__ */ jsx2("div", { className: "bg-primary-light text-primary flex h-9 w-9 shrink-0 items-center justify-center rounded-full", children: /* @__PURE__ */ jsx2(Bot, { size: 16 }) }),
1271
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
1272
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
1273
+ /* @__PURE__ */ jsx2("span", { className: "text-text text-sm font-medium", children: agent.agentName }),
1274
+ agent.webhookCount > 0 ? /* @__PURE__ */ jsxs2(
1275
+ "button",
1276
+ {
1277
+ type: "button",
1278
+ onClick: () => setWebhookDialogAgent(agent),
1279
+ className: "bg-primary-light text-primary ring-primary/30 hover:bg-primary/15 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ring-1 transition",
1280
+ children: [
1281
+ /* @__PURE__ */ jsx2(Webhook, { size: 10 }),
1282
+ agent.webhookCount,
1283
+ " webhook",
1284
+ agent.webhookCount > 1 ? "s" : ""
1285
+ ]
1286
+ }
1287
+ ) : /* @__PURE__ */ jsxs2(
1288
+ "button",
1289
+ {
1290
+ type: "button",
1291
+ onClick: () => setWebhookDialogAgent(agent),
1292
+ className: "bg-warning/10 text-warning ring-warning/30 hover:bg-warning/15 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ring-1 transition",
1293
+ title: "Aucun webhook branch\xE9 \u2014 cliquez pour en ajouter",
1294
+ children: [
1295
+ /* @__PURE__ */ jsx2(AlertTriangle, { size: 10 }),
1296
+ "Aucun webhook"
1297
+ ]
1298
+ }
1299
+ )
1300
+ ] }),
1301
+ /* @__PURE__ */ jsx2("div", { className: "text-text-muted font-mono text-xs", children: agent.agentId })
1302
+ ] }),
1303
+ /* @__PURE__ */ jsx2("div", { className: "text-text-muted shrink-0 text-xs", children: formatDistanceToNow(new Date(agent.createdAt), {
1304
+ addSuffix: true,
1305
+ locale: fr2
1306
+ }) }),
1307
+ /* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-1", children: [
1308
+ /* @__PURE__ */ jsx2(
1309
+ "button",
1310
+ {
1311
+ type: "button",
1312
+ onClick: () => setWebhookDialogAgent(agent),
1313
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-2 transition",
1314
+ title: "G\xE9rer les webhooks",
1315
+ children: /* @__PURE__ */ jsx2(Webhook, { size: 16 })
1316
+ }
1317
+ ),
1318
+ /* @__PURE__ */ jsx2(
1319
+ "button",
1320
+ {
1321
+ type: "button",
1322
+ onClick: () => openEdit(agent),
1323
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-2 transition",
1324
+ title: "Modifier",
1325
+ children: /* @__PURE__ */ jsx2(Pencil2, { size: 16 })
1326
+ }
1327
+ ),
1328
+ /* @__PURE__ */ jsx2(
1329
+ "button",
1330
+ {
1331
+ type: "button",
1332
+ onClick: () => requestDelete(agent),
1333
+ className: "text-text-muted hover:text-danger hover:bg-danger-light rounded-lg p-2 opacity-0 transition group-hover:opacity-100",
1334
+ title: "Supprimer",
1335
+ children: /* @__PURE__ */ jsx2(Trash22, { size: 16 })
1336
+ }
1337
+ )
1338
+ ] })
1339
+ ]
1340
+ },
1341
+ agent._id
1342
+ )) })
1343
+ ] }) })
1344
+ ] });
1345
+ }
1346
+
1347
+ // src/admin/WorkspaceWebhooksPage.tsx
1348
+ import { useEffect as useEffect3, useState as useState3, useCallback as useCallback3 } from "react";
1349
+ import {
1350
+ Plus as Plus3,
1351
+ Pencil as Pencil3,
1352
+ Trash2 as Trash23,
1353
+ Loader2 as Loader23,
1354
+ X as X3,
1355
+ Webhook as Webhook2,
1356
+ CircleCheck as CircleCheck2,
1357
+ CircleX as CircleX2,
1358
+ ExternalLink as ExternalLink2,
1359
+ ArrowLeft as ArrowLeft3,
1360
+ Search as Search2
1361
+ } from "lucide-react";
1362
+ import { formatDistanceToNow as formatDistanceToNow2 } from "date-fns";
1363
+ import { fr as fr3 } from "date-fns/locale";
1364
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1365
+ function WorkspaceWebhooksPage({ onBack }) {
1366
+ const { webhookApi, agentApi } = useWorkspaceApi();
1367
+ const [subscriptions, setSubscriptions] = useState3([]);
1368
+ const [agents, setAgents] = useState3([]);
1369
+ const [loading, setLoading] = useState3(true);
1370
+ const [error, setError] = useState3("");
1371
+ const [deleteId, setDeleteId] = useState3(null);
1372
+ const [searchFilter, setSearchFilter] = useState3("");
1373
+ const [typeFilter, setTypeFilter] = useState3("all");
1374
+ const [showForm, setShowForm] = useState3(false);
1375
+ const [editingId, setEditingId] = useState3(null);
1376
+ const [saving, setSaving] = useState3(false);
1377
+ const [form, setForm] = useState3({
1378
+ name: "",
1379
+ targetUrl: "",
1380
+ secretKey: "x-api-key",
1381
+ secretValue: "",
1382
+ eventTypes: ["*"],
1383
+ active: true,
1384
+ isAgent: false,
1385
+ agentId: ""
1386
+ });
1387
+ const load = useCallback3(async () => {
1388
+ try {
1389
+ setLoading(true);
1390
+ const params = {};
1391
+ if (searchFilter.trim()) params.search = searchFilter.trim();
1392
+ if (typeFilter === "agent") params.isAgent = "true";
1393
+ else if (typeFilter === "standard") params.isAgent = "false";
1394
+ const [data, agentsData] = await Promise.all([
1395
+ webhookApi.list(params),
1396
+ agentApi.list()
1397
+ ]);
1398
+ setSubscriptions(data);
1399
+ setAgents(agentsData);
1400
+ } catch (e) {
1401
+ setError(e.message);
1402
+ } finally {
1403
+ setLoading(false);
1404
+ }
1405
+ }, [searchFilter, typeFilter, webhookApi, agentApi]);
1406
+ useEffect3(() => {
1407
+ const t = setTimeout(() => load(), 300);
1408
+ return () => clearTimeout(t);
1409
+ }, [load]);
1410
+ const resetForm = () => {
1411
+ setForm({
1412
+ name: "",
1413
+ targetUrl: "",
1414
+ secretKey: "x-api-key",
1415
+ secretValue: "",
1416
+ eventTypes: ["*"],
1417
+ active: true,
1418
+ isAgent: false,
1419
+ agentId: ""
1420
+ });
1421
+ setEditingId(null);
1422
+ setShowForm(false);
1423
+ };
1424
+ const openCreateForm = () => {
1425
+ resetForm();
1426
+ setShowForm(true);
1427
+ };
1428
+ const openEditForm = (sub) => {
1429
+ setForm({
1430
+ name: sub.name,
1431
+ targetUrl: sub.targetUrl,
1432
+ secretKey: sub.secretKey,
1433
+ secretValue: "",
1434
+ eventTypes: [...sub.eventTypes],
1435
+ active: sub.active,
1436
+ isAgent: sub.isAgent ?? false,
1437
+ agentId: sub.agentId ?? ""
1438
+ });
1439
+ setEditingId(sub._id);
1440
+ setShowForm(true);
1441
+ };
1442
+ const handleSubmit = async (e) => {
1443
+ e.preventDefault();
1444
+ if (!form.name.trim() || !form.targetUrl.trim()) return;
1445
+ try {
1446
+ setSaving(true);
1447
+ if (editingId) {
1448
+ await webhookApi.update(editingId, {
1449
+ name: form.name,
1450
+ targetUrl: form.targetUrl,
1451
+ secretKey: form.secretKey,
1452
+ eventTypes: form.eventTypes,
1453
+ active: form.active,
1454
+ isAgent: form.isAgent,
1455
+ ...form.isAgent && form.agentId ? { agentId: form.agentId } : {},
1456
+ ...form.secretValue.trim() ? { secretValue: form.secretValue } : {}
1457
+ });
1458
+ } else {
1459
+ await webhookApi.create({
1460
+ name: form.name,
1461
+ targetUrl: form.targetUrl,
1462
+ secretKey: form.secretKey,
1463
+ secretValue: form.secretValue,
1464
+ eventTypes: form.eventTypes,
1465
+ active: form.active,
1466
+ isAgent: form.isAgent,
1467
+ agentId: form.isAgent ? form.agentId : void 0
1468
+ });
1469
+ }
1470
+ resetForm();
1471
+ await load();
1472
+ } catch (e2) {
1473
+ setError(e2.message);
1474
+ } finally {
1475
+ setSaving(false);
1476
+ }
1477
+ };
1478
+ const toggleActive = async (sub) => {
1479
+ try {
1480
+ await webhookApi.update(sub._id, { active: !sub.active });
1481
+ setSubscriptions(
1482
+ (prev) => prev.map((s) => s._id === sub._id ? { ...s, active: !s.active } : s)
1483
+ );
1484
+ } catch (e) {
1485
+ setError(e.message);
1486
+ }
1487
+ };
1488
+ const confirmDelete = (id) => setDeleteId(id);
1489
+ const executeDelete = async () => {
1490
+ if (!deleteId) return;
1491
+ try {
1492
+ await webhookApi.delete(deleteId);
1493
+ setSubscriptions((prev) => prev.filter((s) => s._id !== deleteId));
1494
+ } catch (e) {
1495
+ setError(e.message);
1496
+ }
1497
+ };
1498
+ const toggleEvent = (event) => {
1499
+ setForm((prev) => {
1500
+ if (event === "*") {
1501
+ return { ...prev, eventTypes: ["*"] };
1502
+ }
1503
+ const without = prev.eventTypes.filter((e) => e !== "*" && e !== event);
1504
+ if (prev.eventTypes.includes(event)) {
1505
+ return { ...prev, eventTypes: without.length ? without : ["*"] };
1506
+ }
1507
+ return { ...prev, eventTypes: [...without, event] };
1508
+ });
1509
+ };
1510
+ if (loading && subscriptions.length === 0) {
1511
+ return /* @__PURE__ */ jsx3("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx3(Loader23, { size: 32, className: "text-primary animate-spin" }) });
1512
+ }
1513
+ return /* @__PURE__ */ jsxs3("div", { className: "flex h-full flex-col", children: [
1514
+ /* @__PURE__ */ jsxs3("div", { className: "bg-surface border-border flex shrink-0 items-center gap-4 border-b px-6 py-4", children: [
1515
+ onBack && /* @__PURE__ */ jsx3(
1516
+ "button",
1517
+ {
1518
+ type: "button",
1519
+ onClick: () => onBack(),
1520
+ className: "text-text-muted hover:text-text hover:bg-bg rounded-lg p-1.5 transition",
1521
+ children: /* @__PURE__ */ jsx3(ArrowLeft3, { size: 20 })
1522
+ }
1523
+ ),
1524
+ /* @__PURE__ */ jsx3("div", { className: "bg-primary-light text-primary flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ jsx3(Webhook2, { size: 24 }) }),
1525
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1", children: [
1526
+ /* @__PURE__ */ jsx3("h1", { className: "text-text text-xl font-semibold", children: "Webhook Subscriptions" }),
1527
+ /* @__PURE__ */ jsx3("p", { className: "text-text-secondary text-sm", children: "G\xE9rer les souscriptions pour la distribution d'\xE9v\xE9nements" })
1528
+ ] }),
1529
+ /* @__PURE__ */ jsxs3(
1530
+ "button",
1531
+ {
1532
+ type: "button",
1533
+ onClick: openCreateForm,
1534
+ className: "bg-primary hover:bg-primary-hover flex items-center gap-2 rounded-xl px-4 py-2.5 text-sm font-medium text-white transition",
1535
+ children: [
1536
+ /* @__PURE__ */ jsx3(Plus3, { size: 16 }),
1537
+ " Nouvelle souscription"
1538
+ ]
1539
+ }
1540
+ )
1541
+ ] }),
1542
+ error && /* @__PURE__ */ jsxs3("div", { className: "border-danger/30 bg-danger-light text-danger mx-6 mt-3 rounded-xl border px-4 py-3 text-sm", children: [
1543
+ error,
1544
+ /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => setError(""), className: "ml-2 underline", children: "\xD7" })
1545
+ ] }),
1546
+ /* @__PURE__ */ jsx3(
1547
+ ConfirmModal,
1548
+ {
1549
+ open: !!deleteId,
1550
+ onClose: () => setDeleteId(null),
1551
+ onConfirm: executeDelete,
1552
+ title: "Supprimer ce webhook",
1553
+ description: "Voulez-vous vraiment supprimer ce webhook ?",
1554
+ danger: true
1555
+ }
1556
+ ),
1557
+ /* @__PURE__ */ jsx3("div", { className: "flex-1 overflow-y-auto p-6", children: /* @__PURE__ */ jsxs3("div", { className: "mx-auto max-w-4xl", children: [
1558
+ /* @__PURE__ */ jsxs3("div", { className: "mb-4 flex flex-wrap items-center gap-3", children: [
1559
+ /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
1560
+ /* @__PURE__ */ jsx3(
1561
+ Search2,
1562
+ {
1563
+ size: 16,
1564
+ className: "text-text-muted absolute top-1/2 left-3 -translate-y-1/2"
1565
+ }
1566
+ ),
1567
+ /* @__PURE__ */ jsx3(
1568
+ "input",
1569
+ {
1570
+ value: searchFilter,
1571
+ onChange: (e) => setSearchFilter(e.target.value),
1572
+ placeholder: "Rechercher par nom ou URL...",
1573
+ className: "bg-surface border-border text-text placeholder:text-text-placeholder focus:ring-primary w-72 rounded-xl border py-2 pr-4 pl-10 text-sm focus:ring-2 focus:outline-none"
1574
+ }
1575
+ )
1576
+ ] }),
1577
+ /* @__PURE__ */ jsx3("div", { className: "flex items-center gap-1.5", children: [
1578
+ { key: "all", label: "Tous" },
1579
+ { key: "standard", label: "Standard" },
1580
+ { key: "agent", label: "Agent" }
1581
+ ].map(({ key, label }) => /* @__PURE__ */ jsx3(
1582
+ "button",
1583
+ {
1584
+ type: "button",
1585
+ onClick: () => setTypeFilter(key),
1586
+ className: `rounded-full px-3 py-1.5 text-xs font-medium transition ${typeFilter === key ? "bg-primary text-white" : "bg-bg border-border text-text-secondary hover:text-text hover:bg-border-light border"}`,
1587
+ children: label
1588
+ },
1589
+ key
1590
+ )) }),
1591
+ !loading && /* @__PURE__ */ jsxs3("span", { className: "text-text-muted text-xs", children: [
1592
+ subscriptions.length,
1593
+ " r\xE9sultat",
1594
+ subscriptions.length > 1 ? "s" : ""
1595
+ ] })
1596
+ ] }),
1597
+ showForm && /* @__PURE__ */ jsxs3("div", { className: "bg-surface border-border mb-6 rounded-2xl border p-6 shadow-sm", children: [
1598
+ /* @__PURE__ */ jsxs3("div", { className: "mb-4 flex items-center justify-between", children: [
1599
+ /* @__PURE__ */ jsx3("h2", { className: "text-text text-base font-semibold", children: editingId ? "Modifier la souscription" : "Nouvelle souscription" }),
1600
+ /* @__PURE__ */ jsx3(
1601
+ "button",
1602
+ {
1603
+ type: "button",
1604
+ onClick: resetForm,
1605
+ className: "text-text-muted hover:text-text transition",
1606
+ children: /* @__PURE__ */ jsx3(X3, { size: 20 })
1607
+ }
1608
+ )
1609
+ ] }),
1610
+ /* @__PURE__ */ jsxs3("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
1611
+ /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-2 gap-4", children: [
1612
+ /* @__PURE__ */ jsxs3("div", { children: [
1613
+ /* @__PURE__ */ jsx3("label", { className: "text-text-secondary mb-1 block text-xs", children: "Nom" }),
1614
+ /* @__PURE__ */ jsx3(
1615
+ "input",
1616
+ {
1617
+ value: form.name,
1618
+ onChange: (e) => setForm({ ...form, name: e.target.value }),
1619
+ required: true,
1620
+ placeholder: "Garfield Production",
1621
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1622
+ }
1623
+ )
1624
+ ] }),
1625
+ /* @__PURE__ */ jsxs3("div", { children: [
1626
+ /* @__PURE__ */ jsx3("label", { className: "text-text-secondary mb-1 block text-xs", children: "URL cible" }),
1627
+ /* @__PURE__ */ jsx3(
1628
+ "input",
1629
+ {
1630
+ value: form.targetUrl,
1631
+ onChange: (e) => setForm({ ...form, targetUrl: e.target.value }),
1632
+ required: true,
1633
+ placeholder: "https://api.example.com/webhooks",
1634
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1635
+ }
1636
+ )
1637
+ ] })
1638
+ ] }),
1639
+ /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-2 gap-4", children: [
1640
+ /* @__PURE__ */ jsxs3("div", { children: [
1641
+ /* @__PURE__ */ jsx3("label", { className: "text-text-secondary mb-1 block text-xs", children: "Cl\xE9 du header d'authentification" }),
1642
+ /* @__PURE__ */ jsx3(
1643
+ "input",
1644
+ {
1645
+ value: form.secretKey,
1646
+ onChange: (e) => setForm({ ...form, secretKey: e.target.value }),
1647
+ required: true,
1648
+ placeholder: "x-api-key",
1649
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1650
+ }
1651
+ )
1652
+ ] }),
1653
+ /* @__PURE__ */ jsxs3("div", { children: [
1654
+ /* @__PURE__ */ jsxs3("label", { className: "text-text-secondary mb-1 block text-xs", children: [
1655
+ "Valeur du secret",
1656
+ editingId && /* @__PURE__ */ jsx3("span", { className: "text-text-muted ml-1", children: "(laisser vide pour ne pas modifier)" })
1657
+ ] }),
1658
+ /* @__PURE__ */ jsx3(
1659
+ "input",
1660
+ {
1661
+ value: form.secretValue,
1662
+ onChange: (e) => setForm({ ...form, secretValue: e.target.value }),
1663
+ required: !editingId,
1664
+ type: "password",
1665
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
1666
+ className: "bg-bg border-border text-text placeholder:text-text-placeholder focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none"
1667
+ }
1668
+ )
1669
+ ] })
1670
+ ] }),
1671
+ /* @__PURE__ */ jsxs3("div", { children: [
1672
+ /* @__PURE__ */ jsx3("label", { className: "text-text-secondary mb-2 block text-xs", children: "\xC9v\xE9nements" }),
1673
+ /* @__PURE__ */ jsx3("div", { className: "flex flex-wrap gap-2", children: AVAILABLE_WEBHOOK_EVENTS.map((evt) => /* @__PURE__ */ jsx3(
1674
+ "button",
1675
+ {
1676
+ type: "button",
1677
+ onClick: () => toggleEvent(evt),
1678
+ className: `rounded-full px-3 py-1.5 text-xs font-medium transition ${form.eventTypes.includes(evt) ? "bg-primary text-white" : "bg-bg border-border text-text-secondary hover:text-text hover:bg-border-light border"}`,
1679
+ children: evt === "*" ? "Tous (*)" : evt
1680
+ },
1681
+ evt
1682
+ )) })
1683
+ ] }),
1684
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3", children: [
1685
+ /* @__PURE__ */ jsx3(
1686
+ "button",
1687
+ {
1688
+ type: "button",
1689
+ onClick: () => setForm({ ...form, active: !form.active }),
1690
+ className: `relative h-5 w-10 rounded-full transition ${form.active ? "bg-primary" : "bg-border"}`,
1691
+ children: /* @__PURE__ */ jsx3(
1692
+ "span",
1693
+ {
1694
+ className: `absolute top-0.5 left-0.5 h-4 w-4 rounded-full bg-white shadow transition-transform ${form.active ? "translate-x-5" : ""}`
1695
+ }
1696
+ )
1697
+ }
1698
+ ),
1699
+ /* @__PURE__ */ jsx3("span", { className: "text-text text-sm", children: form.active ? "Active" : "Inactive" })
1700
+ ] }),
1701
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3", children: [
1702
+ /* @__PURE__ */ jsx3(
1703
+ "button",
1704
+ {
1705
+ type: "button",
1706
+ onClick: () => setForm({ ...form, isAgent: !form.isAgent }),
1707
+ className: `relative h-5 w-10 rounded-full transition ${form.isAgent ? "bg-primary" : "bg-border"}`,
1708
+ children: /* @__PURE__ */ jsx3(
1709
+ "span",
1710
+ {
1711
+ className: `absolute top-0.5 left-0.5 h-4 w-4 rounded-full bg-white shadow transition-transform ${form.isAgent ? "translate-x-5" : ""}`
1712
+ }
1713
+ )
1714
+ }
1715
+ ),
1716
+ /* @__PURE__ */ jsx3("span", { className: "text-text text-sm", children: form.isAgent ? "Webhook Agent" : "Webhook standard" })
1717
+ ] }),
1718
+ form.isAgent && /* @__PURE__ */ jsxs3("div", { children: [
1719
+ /* @__PURE__ */ jsx3("label", { className: "text-text-secondary mb-1 block text-xs", children: "Agent" }),
1720
+ agents.length === 0 ? /* @__PURE__ */ jsx3("p", { className: "text-warning py-1 text-xs", children: "Aucun agent disponible. Cr\xE9ez d'abord un agent dans la section Agents." }) : /* @__PURE__ */ jsxs3(
1721
+ "select",
1722
+ {
1723
+ value: form.agentId,
1724
+ onChange: (e) => setForm({ ...form, agentId: e.target.value }),
1725
+ required: true,
1726
+ className: "bg-bg border-border text-text focus:ring-primary w-full rounded-xl border px-4 py-2.5 text-sm focus:ring-2 focus:outline-none",
1727
+ children: [
1728
+ /* @__PURE__ */ jsx3("option", { value: "", children: "-- S\xE9lectionner un agent --" }),
1729
+ agents.map((a) => /* @__PURE__ */ jsxs3("option", { value: a.agentId, children: [
1730
+ a.agentName,
1731
+ " (",
1732
+ a.agentId,
1733
+ ")"
1734
+ ] }, a._id))
1735
+ ]
1736
+ }
1737
+ )
1738
+ ] }),
1739
+ /* @__PURE__ */ jsxs3("div", { className: "flex gap-3 pt-1", children: [
1740
+ /* @__PURE__ */ jsxs3(
1741
+ "button",
1742
+ {
1743
+ type: "submit",
1744
+ disabled: saving,
1745
+ className: "bg-primary hover:bg-primary-hover flex items-center gap-2 rounded-xl px-5 py-2 text-sm font-medium text-white transition disabled:opacity-50",
1746
+ children: [
1747
+ saving && /* @__PURE__ */ jsx3(Loader23, { size: 16, className: "animate-spin" }),
1748
+ editingId ? "Enregistrer" : "Cr\xE9er"
1749
+ ]
1750
+ }
1751
+ ),
1752
+ /* @__PURE__ */ jsx3(
1753
+ "button",
1754
+ {
1755
+ type: "button",
1756
+ onClick: resetForm,
1757
+ className: "bg-bg text-text hover:bg-border-light rounded-xl px-5 py-2 text-sm font-medium transition",
1758
+ children: "Annuler"
1759
+ }
1760
+ )
1761
+ ] })
1762
+ ] })
1763
+ ] }),
1764
+ subscriptions.length === 0 ? /* @__PURE__ */ jsxs3("div", { className: "text-text-muted py-20 text-center", children: [
1765
+ /* @__PURE__ */ jsx3(Webhook2, { size: 40, className: "mx-auto mb-3 opacity-30" }),
1766
+ /* @__PURE__ */ jsx3("p", { className: "text-sm", children: "Aucune souscription webhook." }),
1767
+ /* @__PURE__ */ jsx3("p", { className: "text-text-muted mt-1 text-xs", children: "Cr\xE9ez une souscription pour recevoir les \xE9v\xE9nements workspace." })
1768
+ ] }) : /* @__PURE__ */ jsx3("div", { className: "space-y-3", children: subscriptions.map((sub) => /* @__PURE__ */ jsx3(
1769
+ "div",
1770
+ {
1771
+ className: "bg-surface border-border group rounded-xl border px-5 py-4 transition hover:shadow-md",
1772
+ children: /* @__PURE__ */ jsxs3("div", { className: "flex items-start justify-between gap-4", children: [
1773
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
1774
+ /* @__PURE__ */ jsxs3("div", { className: "mb-1 flex items-center gap-2", children: [
1775
+ /* @__PURE__ */ jsx3("h3", { className: "text-text truncate text-sm font-semibold", children: sub.name }),
1776
+ sub.active ? /* @__PURE__ */ jsxs3("span", { className: "bg-success-light text-success ring-success/30 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs ring-1", children: [
1777
+ /* @__PURE__ */ jsx3(CircleCheck2, { size: 12 }),
1778
+ "Active"
1779
+ ] }) : /* @__PURE__ */ jsxs3("span", { className: "text-text-muted bg-bg border-border inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs", children: [
1780
+ /* @__PURE__ */ jsx3(CircleX2, { size: 12 }),
1781
+ "Inactive"
1782
+ ] }),
1783
+ sub.isAgent && /* @__PURE__ */ jsx3("span", { className: "bg-primary-light text-primary ring-primary/30 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs ring-1", children: "Agent" })
1784
+ ] }),
1785
+ /* @__PURE__ */ jsxs3("div", { className: "text-text-muted mb-2 flex items-center gap-1.5 text-xs", children: [
1786
+ /* @__PURE__ */ jsx3(ExternalLink2, { size: 12, className: "shrink-0" }),
1787
+ /* @__PURE__ */ jsx3("span", { className: "truncate", children: sub.targetUrl })
1788
+ ] }),
1789
+ /* @__PURE__ */ jsx3("div", { className: "flex flex-wrap gap-1.5", children: sub.eventTypes.map((evt) => /* @__PURE__ */ jsx3(
1790
+ "span",
1791
+ {
1792
+ className: "bg-bg text-text-secondary rounded-md px-2 py-0.5 text-xs",
1793
+ children: evt === "*" ? "Tous" : evt
1794
+ },
1795
+ evt
1796
+ )) }),
1797
+ /* @__PURE__ */ jsxs3("p", { className: "text-text-muted mt-2 text-xs", children: [
1798
+ "Authentification : ",
1799
+ sub.secretKey,
1800
+ sub.isAgent && sub.agentId && /* @__PURE__ */ jsxs3(Fragment2, { children: [
1801
+ " \u2022 Agent: ",
1802
+ sub.agentId
1803
+ ] }),
1804
+ " ",
1805
+ "\u2022",
1806
+ " ",
1807
+ formatDistanceToNow2(new Date(sub.createdAt), {
1808
+ addSuffix: true,
1809
+ locale: fr3
1810
+ })
1811
+ ] })
1812
+ ] }),
1813
+ /* @__PURE__ */ jsxs3("div", { className: "flex shrink-0 items-center gap-1", children: [
1814
+ /* @__PURE__ */ jsx3(
1815
+ "button",
1816
+ {
1817
+ type: "button",
1818
+ onClick: () => toggleActive(sub),
1819
+ className: `rounded-lg p-2 transition ${sub.active ? "text-success hover:bg-success-light" : "text-text-muted hover:bg-bg"}`,
1820
+ title: sub.active ? "D\xE9sactiver" : "Activer",
1821
+ children: sub.active ? /* @__PURE__ */ jsx3(CircleCheck2, { size: 16 }) : /* @__PURE__ */ jsx3(CircleX2, { size: 16 })
1822
+ }
1823
+ ),
1824
+ /* @__PURE__ */ jsx3(
1825
+ "button",
1826
+ {
1827
+ type: "button",
1828
+ onClick: () => openEditForm(sub),
1829
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-2 transition",
1830
+ title: "Modifier",
1831
+ children: /* @__PURE__ */ jsx3(Pencil3, { size: 16 })
1832
+ }
1833
+ ),
1834
+ /* @__PURE__ */ jsx3(
1835
+ "button",
1836
+ {
1837
+ type: "button",
1838
+ onClick: () => confirmDelete(sub._id),
1839
+ className: "text-text-muted hover:text-danger hover:bg-danger-light rounded-lg p-2 opacity-0 transition group-hover:opacity-100",
1840
+ title: "Supprimer",
1841
+ children: /* @__PURE__ */ jsx3(Trash23, { size: 16 })
1842
+ }
1843
+ )
1844
+ ] })
1845
+ ] })
1846
+ },
1847
+ sub._id
1848
+ )) })
1849
+ ] }) })
1850
+ ] });
1851
+ }
1852
+ export {
1853
+ AVAILABLE_WEBHOOK_EVENTS,
1854
+ WorkspaceAgentsPage,
1855
+ WorkspaceTypesPage,
1856
+ WorkspaceWebhooksPage
1857
+ };
1
1858
  //# sourceMappingURL=admin.mjs.map