@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.js CHANGED
@@ -1,8 +1,14 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
6
12
  var __copyProps = (to, from, except, desc) => {
7
13
  if (from && typeof from === "object" || typeof from === "function") {
8
14
  for (let key of __getOwnPropNames(from))
@@ -11,9 +17,2388 @@ var __copyProps = (to, from, except, desc) => {
11
17
  }
12
18
  return to;
13
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
14
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
29
 
16
30
  // src/admin.ts
17
31
  var admin_exports = {};
32
+ __export(admin_exports, {
33
+ AVAILABLE_WEBHOOK_EVENTS: () => AVAILABLE_WEBHOOK_EVENTS,
34
+ WorkspaceAgentsPage: () => WorkspaceAgentsPage,
35
+ WorkspaceTypesPage: () => WorkspaceTypesPage,
36
+ WorkspaceWebhooksPage: () => WorkspaceWebhooksPage
37
+ });
18
38
  module.exports = __toCommonJS(admin_exports);
39
+
40
+ // src/admin/WorkspaceTypesPage.tsx
41
+ var import_react8 = require("react");
42
+ var import_lucide_react4 = require("lucide-react");
43
+ var import_date_fns = require("date-fns");
44
+ var import_locale = require("date-fns/locale");
45
+
46
+ // src/hooks/useWorkspaceApi.ts
47
+ var import_react4 = require("react");
48
+
49
+ // src/api/index.ts
50
+ function createWorkspaceApis(client) {
51
+ const workspaceTypeApi = {
52
+ list: () => client.get("/workspace-types"),
53
+ get: (id) => client.get(`/workspace-types/${id}`),
54
+ create: (payload) => client.post("/workspace-types", payload),
55
+ update: (id, payload) => client.patch(`/workspace-types/${id}`, payload),
56
+ delete: (id) => client.delete(`/workspace-types/${id}`)
57
+ };
58
+ const workspaceApi = {
59
+ list: (params) => {
60
+ const cleanParams = Object.fromEntries(
61
+ Object.entries(params || {}).filter(([, v]) => v != null && v !== "" && v !== "all").map(([k, v]) => [k, String(v)])
62
+ );
63
+ const qs = Object.keys(cleanParams).length > 0 ? "?" + new URLSearchParams(cleanParams).toString() : "";
64
+ return client.get(`/workspaces${qs}`);
65
+ },
66
+ get: (id) => client.get(`/workspaces/${id}`),
67
+ create: (payload) => client.post("/workspaces", payload),
68
+ update: (id, payload) => client.patch(`/workspaces/${id}`, payload),
69
+ updateStatus: (id, status) => client.patch(`/workspaces/${id}/status`, { status }),
70
+ delete: (id) => client.delete(`/workspaces/${id}`),
71
+ startTyping: (id) => client.post(`/workspaces/${id}/writing/start`, {}),
72
+ stopTyping: (id) => client.post(`/workspaces/${id}/writing/stop`, {}),
73
+ timeline: (workspaceId, params) => {
74
+ const qs = params ? "?" + new URLSearchParams(
75
+ Object.fromEntries(
76
+ Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
77
+ )
78
+ ).toString() : "";
79
+ return client.get(`/workspaces/${workspaceId}/timeline${qs}`);
80
+ }
81
+ };
82
+ const memberApi = {
83
+ list: (workspaceId) => client.get(`/workspaces/${workspaceId}/members`),
84
+ add: (workspaceId, payload) => client.post(`/workspaces/${workspaceId}/members`, payload),
85
+ updateRole: (workspaceId, memberId, role) => client.patch(`/workspaces/${workspaceId}/members/${memberId}`, { role }),
86
+ remove: (workspaceId, memberId) => client.delete(`/workspaces/${workspaceId}/members/${memberId}`)
87
+ };
88
+ const messageApi = {
89
+ list: (workspaceId, params) => {
90
+ const qs = params ? "?" + new URLSearchParams(
91
+ Object.fromEntries(
92
+ Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
93
+ )
94
+ ).toString() : "";
95
+ return client.get(`/workspaces/${workspaceId}/messages${qs}`);
96
+ },
97
+ send: (workspaceId, payload) => client.post(`/workspaces/${workspaceId}/messages`, payload),
98
+ update: (workspaceId, messageId, payload) => client.patch(`/workspaces/${workspaceId}/messages/${messageId}`, payload),
99
+ delete: (workspaceId, messageId) => client.delete(`/workspaces/${workspaceId}/messages/${messageId}`),
100
+ togglePin: (workspaceId, messageId) => client.patch(`/workspaces/${workspaceId}/messages/${messageId}/pin`, {})
101
+ };
102
+ const artefactApi = {
103
+ upload: async (workspaceId, files, messageId, artefactType) => {
104
+ const formData = new FormData();
105
+ files.forEach((f) => formData.append("files", f));
106
+ if (messageId) formData.append("messageId", messageId);
107
+ if (artefactType) formData.append("type", artefactType);
108
+ return client.upload(`/workspaces/${workspaceId}/artefacts`, formData);
109
+ },
110
+ list: (workspaceId, artefactType) => {
111
+ const query = artefactType ? `?type=${artefactType}` : "";
112
+ return client.get(
113
+ `/workspaces/${workspaceId}/artefacts${query}`
114
+ );
115
+ },
116
+ listPaginated: (workspaceId, params) => {
117
+ const entries = [];
118
+ if (params) {
119
+ if (params.type) entries.push(["type", params.type]);
120
+ if (params.limit != null) entries.push(["limit", String(params.limit)]);
121
+ if (params.offset != null) entries.push(["offset", String(params.offset)]);
122
+ }
123
+ const qs = entries.length > 0 ? "?" + new URLSearchParams(entries).toString() : "";
124
+ return client.get(
125
+ `/workspaces/${workspaceId}/artefacts${qs}`
126
+ );
127
+ },
128
+ delete: (workspaceId, artefactId) => client.delete(`/workspaces/${workspaceId}/artefacts/${artefactId}`),
129
+ getSignedUrl: (fileId) => client.get(`/files/${fileId}/signed-url`),
130
+ getDownloadBlob: (fileId) => client.getBlob(`/files/${fileId}/download`),
131
+ messagesWithArtefacts: (workspaceId, params) => {
132
+ const entries = [];
133
+ if (params) {
134
+ if (params.limit != null) entries.push(["limit", String(params.limit)]);
135
+ if (params.offset != null) entries.push(["offset", String(params.offset)]);
136
+ if (params.sort) entries.push(["sort", params.sort]);
137
+ if (params.members && params.members.length > 0)
138
+ entries.push(["members", params.members.join(",")]);
139
+ if (params.concernsMe) entries.push(["concernsMe", "true"]);
140
+ if (params.pinned) entries.push(["pinned", "true"]);
141
+ }
142
+ const qs = entries.length > 0 ? "?" + new URLSearchParams(entries).toString() : "";
143
+ return client.get(
144
+ `/workspaces/${workspaceId}/messages/with-artefacts${qs}`
145
+ );
146
+ }
147
+ };
148
+ const reactionApi = {
149
+ toggle: (workspaceId, messageId, reaction) => client.post(
150
+ `/workspaces/${workspaceId}/messages/${messageId}/reactions/toggle`,
151
+ { reaction }
152
+ )
153
+ };
154
+ const agentApi = {
155
+ list: (search) => {
156
+ const qs = search ? `?search=${encodeURIComponent(search)}` : "";
157
+ return client.get(`/workspace-agents${qs}`);
158
+ },
159
+ create: (body) => client.post("/workspace-agents", body),
160
+ update: (id, body) => client.patch(`/workspace-agents/${id}`, body),
161
+ delete: (id) => client.delete(`/workspace-agents/${id}`),
162
+ getDeleteInfo: (id) => client.get(`/workspace-agents/${id}/delete-info`)
163
+ };
164
+ const summaryApi = {
165
+ generate: (workspaceId) => client.post(`/workspaces/${workspaceId}/summaries/generate`, {}),
166
+ getLatest: (workspaceId) => client.get(`/workspaces/${workspaceId}/summaries/latest`),
167
+ getHistory: (workspaceId) => client.get(`/workspaces/${workspaceId}/summaries`),
168
+ create: (workspaceId, payload) => client.post(`/workspaces/${workspaceId}/summaries`, payload),
169
+ update: (workspaceId, summaryId, payload) => client.patch(
170
+ `/workspaces/${workspaceId}/summaries/${summaryId}`,
171
+ payload
172
+ )
173
+ };
174
+ const webhookApi = {
175
+ list: (params) => {
176
+ const cleanParams = Object.fromEntries(
177
+ Object.entries(params || {}).filter(([, v]) => v !== void 0 && v !== "")
178
+ );
179
+ const qs = Object.keys(cleanParams).length > 0 ? "?" + new URLSearchParams(cleanParams).toString() : "";
180
+ return client.get(`/webhook-subscriptions${qs}`);
181
+ },
182
+ create: (data) => client.post("/webhook-subscriptions", data),
183
+ update: (id, data) => client.patch(`/webhook-subscriptions/${id}`, data),
184
+ delete: (id) => client.delete(`/webhook-subscriptions/${id}`)
185
+ };
186
+ const syncApi = {
187
+ getAvailableProviders: () => client.get("/workspaces/sync/providers").then((res) => res.providers),
188
+ getStatus: (workspaceId) => client.get(`/workspaces/${workspaceId}/sync-status`),
189
+ toggleSync: (workspaceId, provider, enabled) => client.patch(`/workspaces/${workspaceId}/sync`, { provider, enabled })
190
+ };
191
+ return {
192
+ workspaceTypeApi,
193
+ workspaceApi,
194
+ memberApi,
195
+ messageApi,
196
+ artefactApi,
197
+ reactionApi,
198
+ agentApi,
199
+ summaryApi,
200
+ webhookApi,
201
+ syncApi
202
+ };
203
+ }
204
+
205
+ // src/provider.tsx
206
+ var import_react3 = require("react");
207
+
208
+ // src/socket/client.ts
209
+ var import_socket = require("socket.io-client");
210
+
211
+ // src/theme-context.tsx
212
+ var import_react = require("react");
213
+ var EMPTY = { className: "", style: {} };
214
+ var WorkspaceThemeContext = (0, import_react.createContext)(EMPTY);
215
+ function useWorkspaceThemeAttrs() {
216
+ return (0, import_react.useContext)(WorkspaceThemeContext);
217
+ }
218
+
219
+ // src/styling.ts
220
+ var import_react2 = require("react");
221
+ var import_clsx = require("clsx");
222
+ var import_tailwind_merge = require("tailwind-merge");
223
+ var EMPTY2 = {};
224
+ var WorkspaceStylesContext = (0, import_react2.createContext)(EMPTY2);
225
+ function pickSlot(override, slot) {
226
+ if (!override) return void 0;
227
+ if (typeof override === "string") return slot === "root" ? override : void 0;
228
+ return override[slot];
229
+ }
230
+ function useSlots(keys, local, className) {
231
+ const registry = (0, import_react2.useContext)(WorkspaceStylesContext);
232
+ const keyList = Array.isArray(keys) ? keys : [keys];
233
+ return (slot, ...base) => (0, import_tailwind_merge.twMerge)(
234
+ (0, import_clsx.clsx)(
235
+ ...base,
236
+ ...keyList.map((key) => pickSlot(registry[key], slot)),
237
+ pickSlot(local, slot),
238
+ slot === "root" ? className : void 0
239
+ )
240
+ );
241
+ }
242
+
243
+ // src/components/ui/cn.ts
244
+ var import_clsx2 = require("clsx");
245
+ var import_tailwind_merge2 = require("tailwind-merge");
246
+ function cn(...inputs) {
247
+ return (0, import_tailwind_merge2.twMerge)((0, import_clsx2.clsx)(inputs));
248
+ }
249
+
250
+ // src/provider.tsx
251
+ var import_jsx_runtime = require("react/jsx-runtime");
252
+ var WorkspaceContext = (0, import_react3.createContext)(null);
253
+ function useWorkspaceContext() {
254
+ const ctx = (0, import_react3.useContext)(WorkspaceContext);
255
+ if (!ctx)
256
+ throw new Error(
257
+ "useWorkspaceContext must be used within <WorkspaceProvider>"
258
+ );
259
+ return ctx;
260
+ }
261
+
262
+ // src/hooks/useWorkspaceApi.ts
263
+ function useWorkspaceApi() {
264
+ const { apiClient } = useWorkspaceContext();
265
+ return (0, import_react4.useMemo)(() => createWorkspaceApis(apiClient), [apiClient]);
266
+ }
267
+
268
+ // src/components/ui/ConfirmModal.tsx
269
+ var import_react7 = __toESM(require("react"));
270
+ var import_lucide_react3 = require("lucide-react");
271
+
272
+ // src/components/ui/Modal.tsx
273
+ var import_react5 = require("react");
274
+ var import_react_dom = require("react-dom");
275
+ var import_lucide_react = require("lucide-react");
276
+ var import_jsx_runtime2 = require("react/jsx-runtime");
277
+ var MODAL_SIZE_CLASS = {
278
+ sm: "w-full min-w-0 max-w-sm",
279
+ md: "w-full min-w-0 max-w-md",
280
+ lg: "w-full min-w-0 max-w-lg",
281
+ xl: "w-full min-w-0 max-w-xl",
282
+ "2xl": "w-full min-w-0 max-w-2xl",
283
+ "3xl": "w-full min-w-0 max-w-3xl",
284
+ "4xl": "w-full min-w-0 max-w-4xl",
285
+ "5xl": "w-full min-w-0 max-w-5xl",
286
+ "6xl": "w-full min-w-0 max-w-6xl",
287
+ "7xl": "w-full min-w-0 max-w-7xl",
288
+ full: "w-full min-w-0 max-w-[calc(100vw-2rem)] sm:max-w-[min(96vw,90rem)]"
289
+ };
290
+ function Modal({
291
+ open,
292
+ onClose,
293
+ children,
294
+ zIndexClass = "z-[100]",
295
+ backdropClassName = "bg-black/40 backdrop-blur-sm",
296
+ size,
297
+ panelClassName,
298
+ className,
299
+ closeOnBackdropClick = false,
300
+ closeOnEscape = true,
301
+ ariaLabelledBy: ariaLabelledByProp,
302
+ ariaDescribedBy: ariaDescribedByProp,
303
+ title,
304
+ description,
305
+ titleIcon,
306
+ headingId,
307
+ modalHeaderVariant = "strip",
308
+ hideCloseButton = false,
309
+ headerTrailing,
310
+ closeButtonAriaLabel = "Fermer",
311
+ closeButtonDisabled = false,
312
+ closeButtonClassName,
313
+ closeIconSize = 20,
314
+ headerClassName,
315
+ titleClassName,
316
+ descriptionClassName = "text-text-secondary mt-1 text-sm",
317
+ classNames
318
+ }) {
319
+ const slot = useSlots("modal", classNames, className);
320
+ const [mounted, setMounted] = (0, import_react5.useState)(false);
321
+ const autoHeadingId = (0, import_react5.useId)();
322
+ const autoDescIdByModal = (0, import_react5.useId)();
323
+ const themeAttrs = useWorkspaceThemeAttrs();
324
+ const hasHeader = title != null;
325
+ const resolvedHeadingId = headingId ?? ariaLabelledByProp ?? autoHeadingId;
326
+ const assignAutoDescriptionId = hasHeader && description != null && ariaDescribedByProp === void 0 && (typeof description === "string" || typeof description === "number");
327
+ const resolvedAriaDescribedBy = assignAutoDescriptionId ? autoDescIdByModal : ariaDescribedByProp;
328
+ const resolvedAriaLabelledBy = hasHeader ? ariaLabelledByProp ?? resolvedHeadingId : ariaLabelledByProp;
329
+ (0, import_react5.useEffect)(() => {
330
+ setMounted(true);
331
+ }, []);
332
+ (0, import_react5.useEffect)(() => {
333
+ if (!open) return;
334
+ const prevOverflow = document.body.style.overflow;
335
+ document.body.style.overflow = "hidden";
336
+ return () => {
337
+ document.body.style.overflow = prevOverflow;
338
+ };
339
+ }, [open]);
340
+ (0, import_react5.useEffect)(() => {
341
+ if (!open || !closeOnEscape) return;
342
+ const onKeyDown = (e) => {
343
+ if (e.key === "Escape") onClose();
344
+ };
345
+ window.addEventListener("keydown", onKeyDown);
346
+ return () => window.removeEventListener("keydown", onKeyDown);
347
+ }, [open, closeOnEscape, onClose]);
348
+ const defaultCloseClasses = closeButtonClassName ?? "text-text-secondary hover:text-text rounded p-1 transition-colors";
349
+ const trailing = headerTrailing ?? (!hideCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
350
+ "button",
351
+ {
352
+ type: "button",
353
+ disabled: closeButtonDisabled,
354
+ onClick: onClose,
355
+ className: slot(
356
+ "close",
357
+ "ws-modal__close",
358
+ defaultCloseClasses,
359
+ closeButtonDisabled && "opacity-40"
360
+ ),
361
+ "aria-label": closeButtonAriaLabel,
362
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.X, { size: closeIconSize })
363
+ }
364
+ ) : null);
365
+ const descriptionSlot = description == null ? null : typeof description === "string" || typeof description === "number" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
366
+ "p",
367
+ {
368
+ id: assignAutoDescriptionId ? autoDescIdByModal : void 0,
369
+ className: slot("description", "ws-modal__description", descriptionClassName),
370
+ children: description
371
+ }
372
+ ) : description;
373
+ const headerShellClass = modalHeaderVariant === "strip" ? "border-border flex shrink-0 items-start justify-between gap-3 border-b px-6 py-4" : "mb-4 flex shrink-0 items-start justify-between gap-3";
374
+ const modalHeaderMarkup = hasHeader ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: slot("header", "ws-modal__header", headerShellClass, headerClassName), children: [
375
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex min-w-0 flex-1 items-start gap-3", children: [
376
+ titleIcon != null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mt-0.5 shrink-0", children: titleIcon }) : null,
377
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "min-w-0 flex-1", children: [
378
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
379
+ "h2",
380
+ {
381
+ id: resolvedHeadingId,
382
+ className: slot(
383
+ "title",
384
+ "ws-modal__title",
385
+ "text-text text-lg font-semibold",
386
+ titleClassName
387
+ ),
388
+ children: title
389
+ }
390
+ ),
391
+ descriptionSlot
392
+ ] })
393
+ ] }),
394
+ trailing ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex shrink-0 items-start", children: trailing }) : null
395
+ ] }) : null;
396
+ if (!mounted || !open) return null;
397
+ return (0, import_react_dom.createPortal)(
398
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
399
+ "div",
400
+ {
401
+ className: slot(
402
+ "root",
403
+ "ws-modal",
404
+ "fixed inset-0 flex items-center justify-center p-4",
405
+ themeAttrs.className,
406
+ zIndexClass,
407
+ backdropClassName
408
+ ),
409
+ style: themeAttrs.style,
410
+ onClick: closeOnBackdropClick ? onClose : void 0,
411
+ "aria-hidden": false,
412
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
413
+ "div",
414
+ {
415
+ role: "dialog",
416
+ "aria-modal": "true",
417
+ "aria-labelledby": resolvedAriaLabelledBy,
418
+ "aria-describedby": resolvedAriaDescribedBy,
419
+ className: slot(
420
+ "panel",
421
+ "ws-modal__panel",
422
+ // Solid, opaque panel by default — guarantees the modal is never
423
+ // transparent even if the caller forgets to pass `panelClassName`.
424
+ "bg-surface text-text border-border relative flex max-h-[calc(100vh-2rem)] w-full min-w-0 flex-col overflow-hidden rounded-2xl border shadow-panel animate-ws-scale-in",
425
+ size ? MODAL_SIZE_CLASS[size] : void 0,
426
+ panelClassName
427
+ ),
428
+ onClick: (e) => e.stopPropagation(),
429
+ children: [
430
+ modalHeaderMarkup,
431
+ children
432
+ ]
433
+ }
434
+ )
435
+ }
436
+ ),
437
+ document.body
438
+ );
439
+ }
440
+
441
+ // src/components/ui/Button.tsx
442
+ var import_react6 = __toESM(require("react"));
443
+ var import_lucide_react2 = require("lucide-react");
444
+ var import_jsx_runtime3 = require("react/jsx-runtime");
445
+ var VARIANT_CLASSES = {
446
+ primary: "bg-primary text-primary-contrast hover:bg-primary-hover border border-transparent shadow-xs",
447
+ secondary: "bg-secondary text-secondary-contrast hover:bg-secondary-hover border border-transparent shadow-xs",
448
+ outline: "bg-surface text-text border border-border hover:bg-bg-subtle hover:border-text-muted/40",
449
+ ghost: "bg-transparent text-text-secondary hover:bg-bg-subtle hover:text-text border border-transparent",
450
+ danger: "bg-danger text-white hover:brightness-95 border border-transparent shadow-xs",
451
+ subtle: "bg-primary-light text-primary hover:bg-primary-light/70 border border-transparent"
452
+ };
453
+ var SIZE_CLASSES = {
454
+ xs: "h-7 gap-1 px-2.5 text-xs rounded-lg",
455
+ sm: "h-9 gap-1.5 px-3.5 text-sm rounded-lg",
456
+ md: "h-10 gap-2 px-4 text-sm rounded-xl",
457
+ lg: "h-11 gap-2 px-5 text-[15px] rounded-xl"
458
+ };
459
+ var Button = import_react6.default.forwardRef(
460
+ ({
461
+ variant = "primary",
462
+ size = "md",
463
+ loading = false,
464
+ fullWidth = false,
465
+ leftIcon,
466
+ rightIcon,
467
+ className,
468
+ children,
469
+ disabled,
470
+ type = "button",
471
+ ...props
472
+ }, ref) => {
473
+ const isDisabled = disabled || loading;
474
+ const slot = useSlots("button", void 0, className);
475
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
476
+ "button",
477
+ {
478
+ ref,
479
+ type,
480
+ disabled: isDisabled,
481
+ "data-variant": variant,
482
+ "data-size": size,
483
+ className: slot(
484
+ "root",
485
+ "ws-button",
486
+ `ws-button--${variant}`,
487
+ `ws-button--${size}`,
488
+ "inline-flex items-center justify-center font-medium whitespace-nowrap transition-all duration-150",
489
+ "focus-visible:ring-primary/40 focus-visible:ring-2 focus-visible:outline-none",
490
+ "active:scale-[0.98]",
491
+ "disabled:pointer-events-none disabled:opacity-55",
492
+ VARIANT_CLASSES[variant],
493
+ SIZE_CLASSES[size],
494
+ fullWidth && "w-full"
495
+ ),
496
+ ...props,
497
+ children: [
498
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.Loader2, { className: "animate-spin", size: size === "lg" ? 18 : 16 }) : leftIcon,
499
+ children,
500
+ !loading && rightIcon
501
+ ]
502
+ }
503
+ );
504
+ }
505
+ );
506
+ Button.displayName = "Button";
507
+
508
+ // src/components/ui/ConfirmModal.tsx
509
+ var import_jsx_runtime4 = require("react/jsx-runtime");
510
+ var ConfirmModal = ({
511
+ open,
512
+ onClose,
513
+ onConfirm,
514
+ title,
515
+ description,
516
+ confirmText = "Confirmer",
517
+ cancelText = "Annuler",
518
+ danger = false,
519
+ className,
520
+ classNames
521
+ }) => {
522
+ const [loading, setLoading] = import_react7.default.useState(false);
523
+ const handleConfirm = async () => {
524
+ setLoading(true);
525
+ try {
526
+ await onConfirm();
527
+ } finally {
528
+ setLoading(false);
529
+ onClose();
530
+ }
531
+ };
532
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
533
+ Modal,
534
+ {
535
+ open,
536
+ onClose: () => {
537
+ if (!loading) onClose();
538
+ },
539
+ size: "sm",
540
+ closeOnBackdropClick: !loading,
541
+ closeOnEscape: !loading,
542
+ hideCloseButton: true,
543
+ zIndexClass: "z-[120]",
544
+ className: cn("ws-confirm-modal", className),
545
+ classNames,
546
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "ws-confirm-modal__body flex flex-col gap-5 p-6", children: [
547
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-start gap-3.5", children: [
548
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
549
+ "div",
550
+ {
551
+ className: cn(
552
+ "flex h-11 w-11 flex-shrink-0 items-center justify-center rounded-full",
553
+ danger ? "bg-danger-light" : "bg-primary-light"
554
+ ),
555
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
556
+ import_lucide_react3.AlertTriangle,
557
+ {
558
+ size: 20,
559
+ className: danger ? "text-danger" : "text-primary"
560
+ }
561
+ )
562
+ }
563
+ ),
564
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "min-w-0 flex-1 pt-0.5", children: [
565
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "text-text text-base font-semibold", children: title }),
566
+ description && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "text-text-secondary mt-1.5 text-sm leading-relaxed", children: description })
567
+ ] })
568
+ ] }),
569
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex justify-end gap-2.5", children: [
570
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
571
+ Button,
572
+ {
573
+ variant: "outline",
574
+ size: "sm",
575
+ onClick: onClose,
576
+ disabled: loading,
577
+ children: cancelText
578
+ }
579
+ ),
580
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
581
+ Button,
582
+ {
583
+ variant: danger ? "danger" : "primary",
584
+ size: "sm",
585
+ loading,
586
+ onClick: handleConfirm,
587
+ children: confirmText
588
+ }
589
+ )
590
+ ] })
591
+ ] })
592
+ }
593
+ );
594
+ };
595
+
596
+ // src/admin/WorkspaceTypesPage.tsx
597
+ var import_jsx_runtime5 = require("react/jsx-runtime");
598
+ function WorkspaceTypesPage({ onBack }) {
599
+ const { workspaceTypeApi } = useWorkspaceApi();
600
+ const [types, setTypes] = (0, import_react8.useState)([]);
601
+ const [loading, setLoading] = (0, import_react8.useState)(true);
602
+ const [error, setError] = (0, import_react8.useState)("");
603
+ const [showForm, setShowForm] = (0, import_react8.useState)(false);
604
+ const [editTarget, setEditTarget] = (0, import_react8.useState)(null);
605
+ const [formName, setFormName] = (0, import_react8.useState)("");
606
+ const [formEntities, setFormEntities] = (0, import_react8.useState)([]);
607
+ const [saving, setSaving] = (0, import_react8.useState)(false);
608
+ const [selectedId, setSelectedId] = (0, import_react8.useState)(null);
609
+ const [deleteId, setDeleteId] = (0, import_react8.useState)(null);
610
+ const load = (0, import_react8.useCallback)(async () => {
611
+ try {
612
+ setLoading(true);
613
+ setTypes(await workspaceTypeApi.list());
614
+ } catch (e) {
615
+ setError(e.message);
616
+ } finally {
617
+ setLoading(false);
618
+ }
619
+ }, [workspaceTypeApi]);
620
+ (0, import_react8.useEffect)(() => {
621
+ load();
622
+ }, [load]);
623
+ const toFormEntities = (t) => {
624
+ if (t.entitiesWithSecrets && t.entitiesWithSecrets.length > 0) {
625
+ return t.entitiesWithSecrets.map((e) => ({
626
+ key: e.key,
627
+ value: e.value,
628
+ isSecret: e.isSecret
629
+ }));
630
+ }
631
+ return [];
632
+ };
633
+ const openCreate = () => {
634
+ setEditTarget(null);
635
+ setFormName("");
636
+ setFormEntities([]);
637
+ setShowForm(true);
638
+ };
639
+ const openEdit = (t) => {
640
+ setEditTarget(t);
641
+ setFormName(t.name);
642
+ setFormEntities(toFormEntities(t));
643
+ setShowForm(true);
644
+ };
645
+ const handleSubmit = async (e) => {
646
+ e.preventDefault();
647
+ if (!formName.trim()) return;
648
+ try {
649
+ setSaving(true);
650
+ const entitiesWithSecrets = formEntities.filter((ent) => ent.key.trim()).map((ent) => ({
651
+ key: ent.key.trim(),
652
+ value: ent.value,
653
+ isSecret: ent.isSecret
654
+ }));
655
+ if (editTarget) {
656
+ await workspaceTypeApi.update(editTarget._id, {
657
+ name: formName.trim(),
658
+ entitiesWithSecrets
659
+ });
660
+ } else {
661
+ await workspaceTypeApi.create({
662
+ name: formName.trim(),
663
+ entitiesWithSecrets: entitiesWithSecrets.length > 0 ? entitiesWithSecrets : void 0
664
+ });
665
+ }
666
+ setShowForm(false);
667
+ load();
668
+ } catch (e2) {
669
+ setError(e2.message);
670
+ } finally {
671
+ setSaving(false);
672
+ }
673
+ };
674
+ const confirmDelete = (id) => setDeleteId(id);
675
+ const executeDelete = async () => {
676
+ if (!deleteId) return;
677
+ try {
678
+ await workspaceTypeApi.delete(deleteId);
679
+ load();
680
+ } catch (e) {
681
+ setError(e.message);
682
+ }
683
+ };
684
+ const getDisplayEntities = (t) => {
685
+ return t.entitiesWithSecrets ?? [];
686
+ };
687
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex h-full flex-col", children: [
688
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "bg-surface border-border flex shrink-0 items-center gap-4 border-b px-6 py-4", children: [
689
+ onBack && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
690
+ "button",
691
+ {
692
+ type: "button",
693
+ onClick: () => onBack(),
694
+ className: "text-text-muted hover:text-text hover:bg-bg rounded-lg p-1.5 transition",
695
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ArrowLeft, { size: 20 })
696
+ }
697
+ ),
698
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "bg-primary-light text-primary flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Tag, { size: 24 }) }),
699
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1", children: [
700
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h1", { className: "text-text text-xl font-semibold", children: "Types de Workspace" }),
701
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-text-secondary text-sm", children: "G\xE9rez les cat\xE9gories d'espaces de travail" })
702
+ ] }),
703
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
704
+ "button",
705
+ {
706
+ type: "button",
707
+ onClick: openCreate,
708
+ 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",
709
+ children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Plus, { size: 16 }),
711
+ " Nouveau type"
712
+ ]
713
+ }
714
+ )
715
+ ] }),
716
+ error && /* @__PURE__ */ (0, import_jsx_runtime5.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: [
717
+ error,
718
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick: () => setError(""), className: "ml-2 underline", children: "\xD7" })
719
+ ] }),
720
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
721
+ ConfirmModal,
722
+ {
723
+ open: !!deleteId,
724
+ onClose: () => setDeleteId(null),
725
+ onConfirm: executeDelete,
726
+ title: "Supprimer ce type",
727
+ description: "Voulez-vous vraiment supprimer ce type de workspace ?",
728
+ danger: true
729
+ }
730
+ ),
731
+ showForm && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4 backdrop-blur-sm", children: /* @__PURE__ */ (0, import_jsx_runtime5.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: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mb-4 flex items-center justify-between", children: [
733
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "text-text text-lg font-semibold", children: editTarget ? "Modifier le type" : "Cr\xE9er un type" }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
735
+ "button",
736
+ {
737
+ type: "button",
738
+ onClick: () => setShowForm(false),
739
+ className: "text-text-muted hover:text-text transition",
740
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.X, { size: 20 })
741
+ }
742
+ )
743
+ ] }),
744
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
745
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
746
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "text-text-secondary mb-1.5 block text-sm", children: "Nom" }),
747
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
748
+ "input",
749
+ {
750
+ value: formName,
751
+ onChange: (e) => setFormName(e.target.value),
752
+ placeholder: "ex: incident, projet, client...",
753
+ 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",
754
+ autoFocus: true
755
+ }
756
+ )
757
+ ] }),
758
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
759
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { className: "text-text-secondary mb-1.5 block text-sm", children: "Entit\xE9s" }),
760
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "space-y-2", children: [
761
+ formEntities.map((ent, idx) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
762
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
763
+ "input",
764
+ {
765
+ value: ent.key,
766
+ onChange: (e) => {
767
+ const updated = [...formEntities];
768
+ updated[idx].key = e.target.value;
769
+ setFormEntities(updated);
770
+ },
771
+ placeholder: "Cl\xE9",
772
+ 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"
773
+ }
774
+ ),
775
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
776
+ "input",
777
+ {
778
+ value: ent.value,
779
+ onChange: (e) => {
780
+ const updated = [...formEntities];
781
+ updated[idx].value = e.target.value;
782
+ setFormEntities(updated);
783
+ },
784
+ placeholder: "Ex: noemie-service",
785
+ 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"
786
+ }
787
+ ),
788
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
789
+ "button",
790
+ {
791
+ type: "button",
792
+ onClick: () => {
793
+ const updated = [...formEntities];
794
+ updated[idx].isSecret = !updated[idx].isSecret;
795
+ setFormEntities(updated);
796
+ },
797
+ className: `rounded-lg p-2 transition ${ent.isSecret ? "bg-warning/15 text-warning" : "text-text-muted hover:text-text hover:bg-bg"}`,
798
+ title: ent.isSecret ? "Secret (cliquer pour rendre public)" : "Public (cliquer pour rendre secret)",
799
+ children: ent.isSecret ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Lock, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Unlock, { size: 16 })
800
+ }
801
+ ),
802
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
803
+ "button",
804
+ {
805
+ type: "button",
806
+ onClick: () => setFormEntities(
807
+ (prev) => prev.filter((_, i) => i !== idx)
808
+ ),
809
+ className: "text-text-muted hover:text-danger p-2 transition",
810
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Trash2, { size: 16 })
811
+ }
812
+ )
813
+ ] }, idx)),
814
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
815
+ "button",
816
+ {
817
+ type: "button",
818
+ onClick: () => setFormEntities((prev) => [
819
+ ...prev,
820
+ { key: "", value: "", isSecret: false }
821
+ ]),
822
+ className: "text-primary hover:text-primary-hover flex items-center gap-1 text-sm transition",
823
+ children: [
824
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Plus, { size: 14 }),
825
+ " Ajouter une entit\xE9"
826
+ ]
827
+ }
828
+ )
829
+ ] })
830
+ ] }),
831
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex gap-3 pt-1", children: [
832
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
833
+ "button",
834
+ {
835
+ type: "submit",
836
+ disabled: saving,
837
+ 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",
838
+ children: [
839
+ saving && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Loader2, { size: 16, className: "animate-spin" }),
840
+ editTarget ? "Enregistrer" : "Cr\xE9er"
841
+ ]
842
+ }
843
+ ),
844
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
845
+ "button",
846
+ {
847
+ type: "button",
848
+ onClick: () => setShowForm(false),
849
+ className: "bg-bg text-text hover:bg-border-light flex-1 rounded-xl py-2.5 text-sm font-medium transition",
850
+ children: "Annuler"
851
+ }
852
+ )
853
+ ] })
854
+ ] })
855
+ ] }) }),
856
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex-1 overflow-y-auto p-6", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mx-auto max-w-3xl", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex items-center justify-center py-20", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Loader2, { size: 32, className: "text-primary animate-spin" }) }) : types.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-text-muted py-20 text-center", children: [
857
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Tag, { size: 40, className: "mx-auto mb-3 opacity-30" }),
858
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "Aucun type de workspace cr\xE9\xE9." })
859
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "space-y-2", children: types.map((t) => {
860
+ const displayEntities = getDisplayEntities(t);
861
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
862
+ "div",
863
+ {
864
+ className: "bg-surface border-border group overflow-hidden rounded-xl border transition hover:shadow-md",
865
+ children: [
866
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
867
+ "div",
868
+ {
869
+ className: "flex cursor-pointer items-center justify-between px-5 py-4",
870
+ onClick: () => setSelectedId(selectedId === t._id ? null : t._id),
871
+ children: [
872
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-3", children: [
873
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "bg-primary h-2 w-2 rounded-full" }),
874
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-text font-medium", children: t.name }),
875
+ displayEntities.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-text-muted text-xs", children: [
876
+ displayEntities.length,
877
+ " entit\xE9",
878
+ displayEntities.length !== 1 ? "s" : ""
879
+ ] })
880
+ ] }),
881
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex gap-2 opacity-0 transition group-hover:opacity-100", children: [
882
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
883
+ "button",
884
+ {
885
+ type: "button",
886
+ onClick: (e) => {
887
+ e.stopPropagation();
888
+ openEdit(t);
889
+ },
890
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-2 transition",
891
+ title: "Modifier",
892
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Pencil, { size: 16 })
893
+ }
894
+ ),
895
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
896
+ "button",
897
+ {
898
+ type: "button",
899
+ onClick: (e) => {
900
+ e.stopPropagation();
901
+ confirmDelete(t._id);
902
+ },
903
+ className: "text-text-muted hover:text-danger hover:bg-danger-light rounded-lg p-2 transition",
904
+ title: "Supprimer",
905
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Trash2, { size: 16 })
906
+ }
907
+ )
908
+ ] })
909
+ ]
910
+ }
911
+ ),
912
+ selectedId === t._id && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "border-border-light space-y-2 border-t px-5 pt-0 pb-4", children: [
913
+ displayEntities.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "pt-2", children: [
914
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-text-muted mb-1.5 text-xs", children: "Entit\xE9s" }),
915
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex flex-wrap gap-1.5", children: displayEntities.map((ent) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
916
+ "span",
917
+ {
918
+ className: "bg-bg text-text-secondary inline-flex items-center gap-1.5 rounded-lg px-2 py-1 text-xs",
919
+ children: [
920
+ ent.isSecret && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
921
+ import_lucide_react4.Lock,
922
+ {
923
+ size: 10,
924
+ className: "text-warning shrink-0"
925
+ }
926
+ ),
927
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-text font-medium", children: [
928
+ ent.key,
929
+ ":"
930
+ ] }),
931
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: ent.isSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022" : ent.value })
932
+ ]
933
+ },
934
+ ent.key
935
+ )) })
936
+ ] }),
937
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-text-muted flex items-center gap-2 pt-1 text-xs", children: [
938
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Clock, { size: 12 }),
939
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
940
+ "Cr\xE9\xE9 le",
941
+ " ",
942
+ (0, import_date_fns.format)(
943
+ new Date(t.createdAt),
944
+ "dd MMM yyyy \xE0 HH:mm",
945
+ { locale: import_locale.fr }
946
+ )
947
+ ] })
948
+ ] }),
949
+ t.createdBy && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-text-muted flex items-center gap-2 text-xs", children: [
950
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.User, { size: 12 }),
951
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
952
+ "Cr\xE9\xE9 par ",
953
+ t.createdBy
954
+ ] })
955
+ ] })
956
+ ] })
957
+ ]
958
+ },
959
+ t._id
960
+ );
961
+ }) }) }) })
962
+ ] });
963
+ }
964
+
965
+ // src/admin/WorkspaceAgentsPage.tsx
966
+ var import_react9 = require("react");
967
+ var import_lucide_react5 = require("lucide-react");
968
+ var import_date_fns2 = require("date-fns");
969
+ var import_locale2 = require("date-fns/locale");
970
+
971
+ // src/admin/constants.ts
972
+ var AVAILABLE_WEBHOOK_EVENTS = [
973
+ "*",
974
+ "workspace.created",
975
+ "workspace.updated",
976
+ "workspace.deleted",
977
+ "workspace.statusChanged",
978
+ "workspace.member.added",
979
+ "workspace.member.updated",
980
+ "workspace.member.removed",
981
+ "workspace.message.created",
982
+ "workspace.message.updated",
983
+ "workspace.message.deleted",
984
+ "workspace.member.user.mentioned",
985
+ "workspace.member.agent.mentioned",
986
+ "workspace.member.user.message.replied",
987
+ "workspace.member.agent.message.replied",
988
+ "workspace.message.reaction.created",
989
+ "workspace.message.reaction.deleted",
990
+ "private.workspace.created",
991
+ "private.workspace.message.created",
992
+ "private.workspace.member.added"
993
+ ];
994
+
995
+ // src/admin/WorkspaceAgentsPage.tsx
996
+ var import_jsx_runtime6 = require("react/jsx-runtime");
997
+ function WebhookDialog({ agent, open, onClose }) {
998
+ const { webhookApi } = useWorkspaceApi();
999
+ const webhookDialogTitleId = (0, import_react9.useId)();
1000
+ const [webhooks, setWebhooks] = (0, import_react9.useState)([]);
1001
+ const [loading, setLoading] = (0, import_react9.useState)(true);
1002
+ const [error, setError] = (0, import_react9.useState)("");
1003
+ const [showForm, setShowForm] = (0, import_react9.useState)(false);
1004
+ const [editingId, setEditingId] = (0, import_react9.useState)(null);
1005
+ const [saving, setSaving] = (0, import_react9.useState)(false);
1006
+ const [deleteId, setDeleteId] = (0, import_react9.useState)(null);
1007
+ const [form, setForm] = (0, import_react9.useState)({
1008
+ name: "",
1009
+ targetUrl: "",
1010
+ secretKey: "x-api-key",
1011
+ secretValue: "",
1012
+ eventTypes: ["*"],
1013
+ active: true
1014
+ });
1015
+ const loadWebhooks = (0, import_react9.useCallback)(async () => {
1016
+ try {
1017
+ setLoading(true);
1018
+ const data = await webhookApi.list({ agentId: agent.agentId });
1019
+ setWebhooks(data);
1020
+ } catch (e) {
1021
+ setError(e.message);
1022
+ } finally {
1023
+ setLoading(false);
1024
+ }
1025
+ }, [agent.agentId, webhookApi]);
1026
+ (0, import_react9.useEffect)(() => {
1027
+ if (open) loadWebhooks();
1028
+ }, [open, loadWebhooks]);
1029
+ const resetForm = () => {
1030
+ setForm({
1031
+ name: "",
1032
+ targetUrl: "",
1033
+ secretKey: "x-api-key",
1034
+ secretValue: "",
1035
+ eventTypes: ["*"],
1036
+ active: true
1037
+ });
1038
+ setEditingId(null);
1039
+ setShowForm(false);
1040
+ };
1041
+ const openCreateForm = () => {
1042
+ resetForm();
1043
+ setShowForm(true);
1044
+ };
1045
+ const openEditForm = (sub) => {
1046
+ setForm({
1047
+ name: sub.name,
1048
+ targetUrl: sub.targetUrl,
1049
+ secretKey: sub.secretKey,
1050
+ secretValue: "",
1051
+ eventTypes: [...sub.eventTypes],
1052
+ active: sub.active
1053
+ });
1054
+ setEditingId(sub._id);
1055
+ setShowForm(true);
1056
+ };
1057
+ const handleSubmit = async (e) => {
1058
+ e.preventDefault();
1059
+ if (!form.name.trim() || !form.targetUrl.trim()) return;
1060
+ try {
1061
+ setSaving(true);
1062
+ if (editingId) {
1063
+ await webhookApi.update(editingId, {
1064
+ name: form.name,
1065
+ targetUrl: form.targetUrl,
1066
+ secretKey: form.secretKey,
1067
+ eventTypes: form.eventTypes,
1068
+ active: form.active,
1069
+ isAgent: true,
1070
+ agentId: agent.agentId,
1071
+ ...form.secretValue.trim() ? { secretValue: form.secretValue } : {}
1072
+ });
1073
+ } else {
1074
+ await webhookApi.create({
1075
+ name: form.name,
1076
+ targetUrl: form.targetUrl,
1077
+ secretKey: form.secretKey,
1078
+ secretValue: form.secretValue,
1079
+ eventTypes: form.eventTypes,
1080
+ active: form.active,
1081
+ isAgent: true,
1082
+ agentId: agent.agentId
1083
+ });
1084
+ }
1085
+ resetForm();
1086
+ await loadWebhooks();
1087
+ } catch (e2) {
1088
+ setError(e2.message);
1089
+ } finally {
1090
+ setSaving(false);
1091
+ }
1092
+ };
1093
+ const toggleActive = async (sub) => {
1094
+ try {
1095
+ await webhookApi.update(sub._id, { active: !sub.active });
1096
+ setWebhooks(
1097
+ (prev) => prev.map((s) => s._id === sub._id ? { ...s, active: !s.active } : s)
1098
+ );
1099
+ } catch (e) {
1100
+ setError(e.message);
1101
+ }
1102
+ };
1103
+ const executeDelete = async () => {
1104
+ if (!deleteId) return;
1105
+ try {
1106
+ await webhookApi.delete(deleteId);
1107
+ setWebhooks((prev) => prev.filter((s) => s._id !== deleteId));
1108
+ } catch (e) {
1109
+ setError(e.message);
1110
+ }
1111
+ };
1112
+ const toggleEvent = (event) => {
1113
+ setForm((prev) => {
1114
+ if (event === "*") {
1115
+ return { ...prev, eventTypes: ["*"] };
1116
+ }
1117
+ const without = prev.eventTypes.filter((e) => e !== "*" && e !== event);
1118
+ if (prev.eventTypes.includes(event)) {
1119
+ return { ...prev, eventTypes: without.length ? without : ["*"] };
1120
+ }
1121
+ return { ...prev, eventTypes: [...without, event] };
1122
+ });
1123
+ };
1124
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1125
+ Modal,
1126
+ {
1127
+ open,
1128
+ onClose,
1129
+ zIndexClass: "z-[100]",
1130
+ backdropClassName: "bg-black/40 backdrop-blur-sm",
1131
+ size: "2xl",
1132
+ panelClassName: "border-border bg-surface flex max-h-[85vh] w-full flex-col overflow-hidden rounded-2xl border shadow-xl",
1133
+ className: "p-4",
1134
+ closeOnBackdropClick: true,
1135
+ closeOnEscape: !saving,
1136
+ headingId: webhookDialogTitleId,
1137
+ ariaLabelledBy: webhookDialogTitleId,
1138
+ title: `Webhooks \u2014 ${agent.agentName}`,
1139
+ description: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-text-muted mt-1 font-mono text-xs", children: agent.agentId }),
1140
+ titleClassName: "text-base font-semibold",
1141
+ titleIcon: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "bg-primary-light text-primary flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Webhook, { size: 18 }) }),
1142
+ headerTrailing: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex shrink-0 items-center gap-2", children: [
1143
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1144
+ "button",
1145
+ {
1146
+ type: "button",
1147
+ onClick: openCreateForm,
1148
+ 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",
1149
+ children: [
1150
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Plus, { size: 14 }),
1151
+ " Nouveau"
1152
+ ]
1153
+ }
1154
+ ),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1156
+ "button",
1157
+ {
1158
+ type: "button",
1159
+ onClick: onClose,
1160
+ className: "text-text-muted hover:text-text transition",
1161
+ "aria-label": "Fermer",
1162
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.X, { size: 20 })
1163
+ }
1164
+ )
1165
+ ] }),
1166
+ headerClassName: "items-center",
1167
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-h-0 flex-1 overflow-y-auto p-6", children: [
1168
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "border-danger/30 bg-danger-light text-danger mb-4 rounded-xl border px-4 py-3 text-sm", children: [
1169
+ error,
1170
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: () => setError(""), className: "ml-2 underline", children: "\xD7" })
1171
+ ] }),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1173
+ ConfirmModal,
1174
+ {
1175
+ open: !!deleteId,
1176
+ onClose: () => setDeleteId(null),
1177
+ onConfirm: executeDelete,
1178
+ title: "Supprimer ce webhook",
1179
+ description: "Voulez-vous vraiment supprimer ce webhook ?",
1180
+ danger: true
1181
+ }
1182
+ ),
1183
+ showForm && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "bg-bg border-border mb-4 rounded-xl border p-5", children: [
1184
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mb-3 flex items-center justify-between", children: [
1185
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "text-text text-sm font-semibold", children: editingId ? "Modifier le webhook" : "Nouveau webhook" }),
1186
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1187
+ "button",
1188
+ {
1189
+ type: "button",
1190
+ onClick: resetForm,
1191
+ className: "text-text-muted hover:text-text transition",
1192
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.X, { size: 16 })
1193
+ }
1194
+ )
1195
+ ] }),
1196
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-3", children: [
1197
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-2 gap-3", children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1199
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Nom" }),
1200
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1201
+ "input",
1202
+ {
1203
+ value: form.name,
1204
+ onChange: (e) => setForm({ ...form, name: e.target.value }),
1205
+ required: true,
1206
+ placeholder: "Mon webhook agent",
1207
+ 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"
1208
+ }
1209
+ )
1210
+ ] }),
1211
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1212
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "URL cible" }),
1213
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1214
+ "input",
1215
+ {
1216
+ value: form.targetUrl,
1217
+ onChange: (e) => setForm({ ...form, targetUrl: e.target.value }),
1218
+ required: true,
1219
+ placeholder: "https://api.example.com/webhooks",
1220
+ 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"
1221
+ }
1222
+ )
1223
+ ] })
1224
+ ] }),
1225
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-2 gap-3", children: [
1226
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1227
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Cl\xE9 du header" }),
1228
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1229
+ "input",
1230
+ {
1231
+ value: form.secretKey,
1232
+ onChange: (e) => setForm({ ...form, secretKey: e.target.value }),
1233
+ required: true,
1234
+ placeholder: "x-api-key",
1235
+ 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"
1236
+ }
1237
+ )
1238
+ ] }),
1239
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1240
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "text-text-secondary mb-1 block text-xs", children: [
1241
+ "Secret",
1242
+ " ",
1243
+ editingId && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-text-muted", children: "(vide = inchang\xE9)" })
1244
+ ] }),
1245
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1246
+ "input",
1247
+ {
1248
+ value: form.secretValue,
1249
+ onChange: (e) => setForm({ ...form, secretValue: e.target.value }),
1250
+ required: !editingId,
1251
+ type: "password",
1252
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
1253
+ 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"
1254
+ }
1255
+ )
1256
+ ] })
1257
+ ] }),
1258
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1259
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1.5 block text-xs", children: "\xC9v\xE9nements" }),
1260
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-1.5", children: AVAILABLE_WEBHOOK_EVENTS.map((evt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1261
+ "button",
1262
+ {
1263
+ type: "button",
1264
+ onClick: () => toggleEvent(evt),
1265
+ 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"}`,
1266
+ children: evt === "*" ? "Tous (*)" : evt
1267
+ },
1268
+ evt
1269
+ )) })
1270
+ ] }),
1271
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-3", children: [
1272
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1273
+ "button",
1274
+ {
1275
+ type: "button",
1276
+ onClick: () => setForm({ ...form, active: !form.active }),
1277
+ className: `relative h-5 w-10 rounded-full transition ${form.active ? "bg-primary" : "bg-border"}`,
1278
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1279
+ "span",
1280
+ {
1281
+ className: `absolute top-0.5 left-0.5 h-4 w-4 rounded-full bg-white shadow transition-transform ${form.active ? "translate-x-5" : ""}`
1282
+ }
1283
+ )
1284
+ }
1285
+ ),
1286
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-text text-sm", children: form.active ? "Active" : "Inactive" })
1287
+ ] }),
1288
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-2 pt-1", children: [
1289
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1290
+ "button",
1291
+ {
1292
+ type: "submit",
1293
+ disabled: saving,
1294
+ 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",
1295
+ children: [
1296
+ saving && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Loader2, { size: 14, className: "animate-spin" }),
1297
+ editingId ? "Enregistrer" : "Cr\xE9er"
1298
+ ]
1299
+ }
1300
+ ),
1301
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1302
+ "button",
1303
+ {
1304
+ type: "button",
1305
+ onClick: resetForm,
1306
+ className: "bg-bg text-text hover:bg-border-light rounded-xl px-4 py-2 text-sm font-medium transition",
1307
+ children: "Annuler"
1308
+ }
1309
+ )
1310
+ ] })
1311
+ ] })
1312
+ ] }),
1313
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex justify-center py-10", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Loader2, { size: 20, className: "text-primary animate-spin" }) }) : webhooks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-text-muted py-12 text-center", children: [
1314
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Webhook, { size: 32, className: "mx-auto mb-2 opacity-30" }),
1315
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm", children: "Aucun webhook branch\xE9 \xE0 cet agent." }),
1316
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-text-muted mt-1 text-xs", children: "Cr\xE9ez un webhook pour que l'agent puisse recevoir les \xE9v\xE9nements." })
1317
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "space-y-2", children: webhooks.map((sub) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1318
+ "div",
1319
+ {
1320
+ className: "bg-bg border-border group rounded-xl border px-4 py-3 transition hover:shadow-sm",
1321
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-start justify-between gap-3", children: [
1322
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0 flex-1", children: [
1323
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mb-1 flex items-center gap-2", children: [
1324
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h4", { className: "text-text truncate text-sm font-medium", children: sub.name }),
1325
+ sub.active ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
1326
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.CircleCheck, { size: 10 }),
1327
+ "Active"
1328
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
1329
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.CircleX, { size: 10 }),
1330
+ "Inactive"
1331
+ ] })
1332
+ ] }),
1333
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-text-muted mb-1.5 flex items-center gap-1 text-xs", children: [
1334
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.ExternalLink, { size: 10, className: "shrink-0" }),
1335
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate", children: sub.targetUrl })
1336
+ ] }),
1337
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-1", children: sub.eventTypes.map((evt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1338
+ "span",
1339
+ {
1340
+ className: "bg-surface text-text-secondary rounded px-1.5 py-0.5 text-xs",
1341
+ children: evt === "*" ? "Tous" : evt
1342
+ },
1343
+ evt
1344
+ )) })
1345
+ ] }),
1346
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex shrink-0 items-center gap-1", children: [
1347
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1348
+ "button",
1349
+ {
1350
+ type: "button",
1351
+ onClick: () => toggleActive(sub),
1352
+ className: `rounded-lg p-1.5 transition ${sub.active ? "text-success hover:bg-success-light" : "text-text-muted hover:bg-bg"}`,
1353
+ title: sub.active ? "D\xE9sactiver" : "Activer",
1354
+ children: sub.active ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.CircleCheck, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.CircleX, { size: 14 })
1355
+ }
1356
+ ),
1357
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1358
+ "button",
1359
+ {
1360
+ type: "button",
1361
+ onClick: () => openEditForm(sub),
1362
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-1.5 transition",
1363
+ title: "Modifier",
1364
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Pencil, { size: 14 })
1365
+ }
1366
+ ),
1367
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1368
+ "button",
1369
+ {
1370
+ type: "button",
1371
+ onClick: () => setDeleteId(sub._id),
1372
+ className: "text-text-muted hover:text-danger hover:bg-danger-light rounded-lg p-1.5 opacity-0 transition group-hover:opacity-100",
1373
+ title: "Supprimer",
1374
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Trash2, { size: 14 })
1375
+ }
1376
+ )
1377
+ ] })
1378
+ ] })
1379
+ },
1380
+ sub._id
1381
+ )) })
1382
+ ] })
1383
+ }
1384
+ );
1385
+ }
1386
+ function WorkspaceAgentsPage({ onBack }) {
1387
+ const { agentApi, webhookApi } = useWorkspaceApi();
1388
+ const [agents, setAgents] = (0, import_react9.useState)([]);
1389
+ const [loading, setLoading] = (0, import_react9.useState)(true);
1390
+ const [error, setError] = (0, import_react9.useState)("");
1391
+ const [search, setSearch] = (0, import_react9.useState)("");
1392
+ const [showForm, setShowForm] = (0, import_react9.useState)(false);
1393
+ const [editingAgent, setEditingAgent] = (0, import_react9.useState)(null);
1394
+ const [saving, setSaving] = (0, import_react9.useState)(false);
1395
+ const [form, setForm] = (0, import_react9.useState)({ agentId: "", agentName: "" });
1396
+ const [webhookForm, setWebhookForm] = (0, import_react9.useState)({
1397
+ name: "",
1398
+ targetUrl: "",
1399
+ secretKey: "x-api-key",
1400
+ secretValue: "",
1401
+ eventTypes: ["*"],
1402
+ active: true
1403
+ });
1404
+ const [webhookNameTouched, setWebhookNameTouched] = (0, import_react9.useState)(false);
1405
+ const [deleteAgent, setDeleteAgent] = (0, import_react9.useState)(null);
1406
+ const [deleteInfo, setDeleteInfo] = (0, import_react9.useState)(null);
1407
+ const [loadingDeleteInfo, setLoadingDeleteInfo] = (0, import_react9.useState)(false);
1408
+ const [webhookDialogAgent, setWebhookDialogAgent] = (0, import_react9.useState)(null);
1409
+ const load = (0, import_react9.useCallback)(async () => {
1410
+ try {
1411
+ setLoading(true);
1412
+ const data = await agentApi.list(search.trim() || void 0);
1413
+ setAgents(data);
1414
+ } catch (e) {
1415
+ setError(e.message);
1416
+ } finally {
1417
+ setLoading(false);
1418
+ }
1419
+ }, [search, agentApi]);
1420
+ (0, import_react9.useEffect)(() => {
1421
+ const t = setTimeout(() => load(), 300);
1422
+ return () => clearTimeout(t);
1423
+ }, [load]);
1424
+ const resetForm = () => {
1425
+ setForm({ agentId: "", agentName: "" });
1426
+ setWebhookForm({
1427
+ name: "",
1428
+ targetUrl: "",
1429
+ secretKey: "x-api-key",
1430
+ secretValue: "",
1431
+ eventTypes: ["*"],
1432
+ active: true
1433
+ });
1434
+ setWebhookNameTouched(false);
1435
+ setEditingAgent(null);
1436
+ setShowForm(false);
1437
+ };
1438
+ const openCreate = () => {
1439
+ setForm({ agentId: "", agentName: "" });
1440
+ setEditingAgent(null);
1441
+ setShowForm(true);
1442
+ };
1443
+ const openEdit = (agent) => {
1444
+ setForm({ agentId: agent.agentId, agentName: agent.agentName });
1445
+ setEditingAgent(agent);
1446
+ setShowForm(true);
1447
+ };
1448
+ const toggleCreateEvent = (event) => {
1449
+ setWebhookForm((prev) => {
1450
+ if (event === "*") {
1451
+ return { ...prev, eventTypes: ["*"] };
1452
+ }
1453
+ const without = prev.eventTypes.filter((e) => e !== "*" && e !== event);
1454
+ if (prev.eventTypes.includes(event)) {
1455
+ return { ...prev, eventTypes: without.length ? without : ["*"] };
1456
+ }
1457
+ return { ...prev, eventTypes: [...without, event] };
1458
+ });
1459
+ };
1460
+ const handleSubmit = async (e) => {
1461
+ e.preventDefault();
1462
+ if (!form.agentName.trim()) return;
1463
+ if (!editingAgent && !form.agentId.trim()) return;
1464
+ if (!editingAgent && (!webhookForm.name.trim() || !webhookForm.targetUrl.trim() || !webhookForm.secretValue.trim()))
1465
+ return;
1466
+ try {
1467
+ setSaving(true);
1468
+ if (editingAgent) {
1469
+ await agentApi.update(editingAgent._id, {
1470
+ agentName: form.agentName
1471
+ });
1472
+ resetForm();
1473
+ await load();
1474
+ } else {
1475
+ const created = await agentApi.create({
1476
+ agentId: form.agentId.trim(),
1477
+ agentName: form.agentName.trim()
1478
+ });
1479
+ await webhookApi.create({
1480
+ name: webhookForm.name.trim(),
1481
+ targetUrl: webhookForm.targetUrl.trim(),
1482
+ secretKey: webhookForm.secretKey.trim(),
1483
+ secretValue: webhookForm.secretValue.trim(),
1484
+ eventTypes: webhookForm.eventTypes,
1485
+ active: webhookForm.active,
1486
+ isAgent: true,
1487
+ agentId: created.agentId
1488
+ });
1489
+ resetForm();
1490
+ await load();
1491
+ }
1492
+ } catch (e2) {
1493
+ setError(e2.message);
1494
+ } finally {
1495
+ setSaving(false);
1496
+ }
1497
+ };
1498
+ const requestDelete = async (agent) => {
1499
+ setDeleteAgent(agent);
1500
+ setDeleteInfo(null);
1501
+ setLoadingDeleteInfo(true);
1502
+ try {
1503
+ const info = await agentApi.getDeleteInfo(agent._id);
1504
+ setDeleteInfo(info);
1505
+ } catch {
1506
+ setDeleteInfo({ webhookCount: 0, memberCount: 0 });
1507
+ } finally {
1508
+ setLoadingDeleteInfo(false);
1509
+ }
1510
+ };
1511
+ const handleDelete = async () => {
1512
+ if (!deleteAgent) return;
1513
+ try {
1514
+ await agentApi.delete(deleteAgent._id);
1515
+ setAgents((prev) => prev.filter((a) => a._id !== deleteAgent._id));
1516
+ } catch (e) {
1517
+ setError(e.message);
1518
+ }
1519
+ };
1520
+ const buildDeleteDescription = () => {
1521
+ if (loadingDeleteInfo) {
1522
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex items-center gap-2", children: [
1523
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Loader2, { size: 14, className: "animate-spin" }),
1524
+ "Chargement des informations..."
1525
+ ] });
1526
+ }
1527
+ if (!deleteInfo) return "Voulez-vous vraiment supprimer cet agent ?";
1528
+ const parts = [];
1529
+ if (deleteInfo.webhookCount > 0) {
1530
+ parts.push(
1531
+ `${deleteInfo.webhookCount} webhook${deleteInfo.webhookCount > 1 ? "s" : ""}`
1532
+ );
1533
+ }
1534
+ if (deleteInfo.memberCount > 0) {
1535
+ parts.push(
1536
+ `membre de ${deleteInfo.memberCount} workspace${deleteInfo.memberCount > 1 ? "s" : ""}`
1537
+ );
1538
+ }
1539
+ if (parts.length === 0) {
1540
+ return `Voulez-vous vraiment supprimer l'agent "${deleteAgent?.agentName}" ?`;
1541
+ }
1542
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1543
+ `Voulez-vous vraiment supprimer l'agent "`,
1544
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("strong", { children: deleteAgent?.agentName }),
1545
+ '" ? Cela supprimera \xE9galement :',
1546
+ " ",
1547
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-danger font-medium", children: parts.join(" et ") }),
1548
+ "."
1549
+ ] });
1550
+ };
1551
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex h-full flex-col", children: [
1552
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "bg-surface border-border flex shrink-0 items-center gap-4 border-b px-6 py-4", children: [
1553
+ onBack && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1554
+ "button",
1555
+ {
1556
+ type: "button",
1557
+ onClick: () => onBack(),
1558
+ className: "text-text-muted hover:text-text hover:bg-bg rounded-lg p-1.5 transition",
1559
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.ArrowLeft, { size: 20 })
1560
+ }
1561
+ ),
1562
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "bg-primary-light text-primary flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Bot, { size: 24 }) }),
1563
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex-1", children: [
1564
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h1", { className: "text-text text-xl font-semibold", children: "Agents Workspace" }),
1565
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-text-secondary text-sm", children: "G\xE9rer les agents disponibles pour les workspaces" })
1566
+ ] }),
1567
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1568
+ "button",
1569
+ {
1570
+ type: "button",
1571
+ onClick: openCreate,
1572
+ 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",
1573
+ children: [
1574
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Plus, { size: 16 }),
1575
+ " Nouvel agent"
1576
+ ]
1577
+ }
1578
+ )
1579
+ ] }),
1580
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.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: [
1581
+ error,
1582
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", onClick: () => setError(""), className: "ml-2 underline", children: "\xD7" })
1583
+ ] }),
1584
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1585
+ ConfirmModal,
1586
+ {
1587
+ open: !!deleteAgent,
1588
+ onClose: () => setDeleteAgent(null),
1589
+ onConfirm: handleDelete,
1590
+ title: "Supprimer cet agent",
1591
+ description: buildDeleteDescription(),
1592
+ danger: true
1593
+ }
1594
+ ),
1595
+ webhookDialogAgent && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1596
+ WebhookDialog,
1597
+ {
1598
+ agent: webhookDialogAgent,
1599
+ open: true,
1600
+ onClose: () => setWebhookDialogAgent(null)
1601
+ }
1602
+ ),
1603
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex-1 overflow-y-auto p-6", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-auto max-w-3xl", children: [
1604
+ showForm && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "bg-surface border-border mb-6 rounded-2xl border p-6 shadow-sm", children: [
1605
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mb-4 flex items-center justify-between", children: [
1606
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "text-text text-base font-semibold", children: editingAgent ? "Modifier l'agent" : "Nouvel agent" }),
1607
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1608
+ "button",
1609
+ {
1610
+ type: "button",
1611
+ onClick: resetForm,
1612
+ className: "text-text-muted hover:text-text transition",
1613
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.X, { size: 20 })
1614
+ }
1615
+ )
1616
+ ] }),
1617
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
1618
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
1619
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1620
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "text-text-secondary mb-1 block text-xs", children: [
1621
+ "Agent ID",
1622
+ " ",
1623
+ editingAgent && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-text-muted", children: "(non modifiable)" })
1624
+ ] }),
1625
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1626
+ "input",
1627
+ {
1628
+ value: form.agentId,
1629
+ onChange: (e) => setForm({ ...form, agentId: e.target.value }),
1630
+ required: !editingAgent,
1631
+ disabled: !!editingAgent,
1632
+ placeholder: "ex: james-agent",
1633
+ pattern: "^[a-z0-9-]+$",
1634
+ title: "Uniquement lettres minuscules, chiffres et tirets",
1635
+ 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"
1636
+ }
1637
+ )
1638
+ ] }),
1639
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1640
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Nom d'affichage" }),
1641
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1642
+ "input",
1643
+ {
1644
+ value: form.agentName,
1645
+ onChange: (e) => {
1646
+ const newName = e.target.value;
1647
+ setForm({ ...form, agentName: newName });
1648
+ if (!editingAgent && !webhookNameTouched) {
1649
+ setWebhookForm((prev) => ({
1650
+ ...prev,
1651
+ name: newName.trim() ? `Webhook ${newName.trim()}` : ""
1652
+ }));
1653
+ }
1654
+ },
1655
+ required: true,
1656
+ autoFocus: !!editingAgent,
1657
+ placeholder: "ex: James",
1658
+ 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"
1659
+ }
1660
+ )
1661
+ ] })
1662
+ ] }),
1663
+ editingAgent && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-warning text-xs", children: "\u26A0 La modification du nom sera propag\xE9e \xE0 tous les membres de workspace ayant cet agent." }),
1664
+ !editingAgent && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "border-border mt-2 border-t pt-4", children: [
1665
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mb-3 flex items-center gap-2", children: [
1666
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "bg-primary-light text-primary flex h-7 w-7 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Webhook, { size: 14 }) }),
1667
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { className: "text-text text-sm font-semibold", children: "Webhook de l'agent" })
1668
+ ] }),
1669
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
1670
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1671
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Nom du webhook" }),
1672
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1673
+ "input",
1674
+ {
1675
+ value: webhookForm.name,
1676
+ onChange: (e) => {
1677
+ setWebhookNameTouched(true);
1678
+ setWebhookForm({
1679
+ ...webhookForm,
1680
+ name: e.target.value
1681
+ });
1682
+ },
1683
+ required: true,
1684
+ placeholder: "ex: Webhook James",
1685
+ 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"
1686
+ }
1687
+ )
1688
+ ] }),
1689
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1690
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "URL cible" }),
1691
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1692
+ "input",
1693
+ {
1694
+ value: webhookForm.targetUrl,
1695
+ onChange: (e) => setWebhookForm({
1696
+ ...webhookForm,
1697
+ targetUrl: e.target.value
1698
+ }),
1699
+ required: true,
1700
+ placeholder: "https://api.example.com/webhooks",
1701
+ 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"
1702
+ }
1703
+ )
1704
+ ] })
1705
+ ] }),
1706
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 grid grid-cols-2 gap-4", children: [
1707
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1708
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Cl\xE9 du header" }),
1709
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1710
+ "input",
1711
+ {
1712
+ value: webhookForm.secretKey,
1713
+ onChange: (e) => setWebhookForm({
1714
+ ...webhookForm,
1715
+ secretKey: e.target.value
1716
+ }),
1717
+ required: true,
1718
+ placeholder: "x-api-key",
1719
+ 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"
1720
+ }
1721
+ )
1722
+ ] }),
1723
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { children: [
1724
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Secret" }),
1725
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1726
+ "input",
1727
+ {
1728
+ value: webhookForm.secretValue,
1729
+ onChange: (e) => setWebhookForm({
1730
+ ...webhookForm,
1731
+ secretValue: e.target.value
1732
+ }),
1733
+ required: true,
1734
+ type: "password",
1735
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
1736
+ 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"
1737
+ }
1738
+ )
1739
+ ] })
1740
+ ] }),
1741
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3", children: [
1742
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "text-text-secondary mb-1.5 block text-xs", children: "\xC9v\xE9nements" }),
1743
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex flex-wrap gap-1.5", children: AVAILABLE_WEBHOOK_EVENTS.map((evt) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1744
+ "button",
1745
+ {
1746
+ type: "button",
1747
+ onClick: () => toggleCreateEvent(evt),
1748
+ 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"}`,
1749
+ children: evt === "*" ? "Tous (*)" : evt
1750
+ },
1751
+ evt
1752
+ )) })
1753
+ ] }),
1754
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mt-3 flex items-center gap-3", children: [
1755
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1756
+ "button",
1757
+ {
1758
+ type: "button",
1759
+ onClick: () => setWebhookForm({
1760
+ ...webhookForm,
1761
+ active: !webhookForm.active
1762
+ }),
1763
+ className: `relative h-5 w-10 rounded-full transition ${webhookForm.active ? "bg-primary" : "bg-border"}`,
1764
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1765
+ "span",
1766
+ {
1767
+ className: `absolute top-0.5 left-0.5 h-4 w-4 rounded-full bg-white shadow transition-transform ${webhookForm.active ? "translate-x-5" : ""}`
1768
+ }
1769
+ )
1770
+ }
1771
+ ),
1772
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-text text-sm", children: webhookForm.active ? "Webhook actif" : "Webhook inactif" })
1773
+ ] })
1774
+ ] }) }),
1775
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex gap-3 pt-1", children: [
1776
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1777
+ "button",
1778
+ {
1779
+ type: "submit",
1780
+ disabled: saving,
1781
+ 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",
1782
+ children: [
1783
+ saving ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Loader2, { size: 16, className: "animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Check, { size: 16 }),
1784
+ editingAgent ? "Enregistrer" : "Cr\xE9er l'agent + webhook"
1785
+ ]
1786
+ }
1787
+ ),
1788
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1789
+ "button",
1790
+ {
1791
+ type: "button",
1792
+ onClick: resetForm,
1793
+ className: "bg-bg text-text hover:bg-border-light rounded-xl px-5 py-2 text-sm font-medium transition",
1794
+ children: "Annuler"
1795
+ }
1796
+ )
1797
+ ] })
1798
+ ] })
1799
+ ] }),
1800
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative mb-4", children: [
1801
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1802
+ import_lucide_react5.Search,
1803
+ {
1804
+ size: 16,
1805
+ className: "text-text-muted absolute top-1/2 left-3 -translate-y-1/2"
1806
+ }
1807
+ ),
1808
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1809
+ "input",
1810
+ {
1811
+ value: search,
1812
+ onChange: (e) => setSearch(e.target.value),
1813
+ placeholder: "Rechercher un agent...",
1814
+ 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"
1815
+ }
1816
+ )
1817
+ ] }),
1818
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex justify-center py-10", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Loader2, { size: 24, className: "text-primary animate-spin" }) }) : agents.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-text-muted py-20 text-center", children: [
1819
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Bot, { size: 40, className: "mx-auto mb-3 opacity-30" }),
1820
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm", children: "Aucun agent trouv\xE9." }),
1821
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-text-muted mt-1 text-xs", children: "Cr\xE9ez un agent pour le rendre disponible dans les workspaces." })
1822
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "space-y-2", children: agents.map((agent) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1823
+ "div",
1824
+ {
1825
+ className: "bg-surface border-border group flex items-center gap-4 rounded-xl border px-5 py-4 transition hover:shadow-md",
1826
+ children: [
1827
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "bg-primary-light text-primary flex h-9 w-9 shrink-0 items-center justify-center rounded-full", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Bot, { size: 16 }) }),
1828
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "min-w-0 flex-1", children: [
1829
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center gap-2", children: [
1830
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-text text-sm font-medium", children: agent.agentName }),
1831
+ agent.webhookCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1832
+ "button",
1833
+ {
1834
+ type: "button",
1835
+ onClick: () => setWebhookDialogAgent(agent),
1836
+ 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",
1837
+ children: [
1838
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Webhook, { size: 10 }),
1839
+ agent.webhookCount,
1840
+ " webhook",
1841
+ agent.webhookCount > 1 ? "s" : ""
1842
+ ]
1843
+ }
1844
+ ) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1845
+ "button",
1846
+ {
1847
+ type: "button",
1848
+ onClick: () => setWebhookDialogAgent(agent),
1849
+ 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",
1850
+ title: "Aucun webhook branch\xE9 \u2014 cliquez pour en ajouter",
1851
+ children: [
1852
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.AlertTriangle, { size: 10 }),
1853
+ "Aucun webhook"
1854
+ ]
1855
+ }
1856
+ )
1857
+ ] }),
1858
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "text-text-muted font-mono text-xs", children: agent.agentId })
1859
+ ] }),
1860
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "text-text-muted shrink-0 text-xs", children: (0, import_date_fns2.formatDistanceToNow)(new Date(agent.createdAt), {
1861
+ addSuffix: true,
1862
+ locale: import_locale2.fr
1863
+ }) }),
1864
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex shrink-0 items-center gap-1", children: [
1865
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1866
+ "button",
1867
+ {
1868
+ type: "button",
1869
+ onClick: () => setWebhookDialogAgent(agent),
1870
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-2 transition",
1871
+ title: "G\xE9rer les webhooks",
1872
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Webhook, { size: 16 })
1873
+ }
1874
+ ),
1875
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1876
+ "button",
1877
+ {
1878
+ type: "button",
1879
+ onClick: () => openEdit(agent),
1880
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-2 transition",
1881
+ title: "Modifier",
1882
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Pencil, { size: 16 })
1883
+ }
1884
+ ),
1885
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1886
+ "button",
1887
+ {
1888
+ type: "button",
1889
+ onClick: () => requestDelete(agent),
1890
+ className: "text-text-muted hover:text-danger hover:bg-danger-light rounded-lg p-2 opacity-0 transition group-hover:opacity-100",
1891
+ title: "Supprimer",
1892
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react5.Trash2, { size: 16 })
1893
+ }
1894
+ )
1895
+ ] })
1896
+ ]
1897
+ },
1898
+ agent._id
1899
+ )) })
1900
+ ] }) })
1901
+ ] });
1902
+ }
1903
+
1904
+ // src/admin/WorkspaceWebhooksPage.tsx
1905
+ var import_react10 = require("react");
1906
+ var import_lucide_react6 = require("lucide-react");
1907
+ var import_date_fns3 = require("date-fns");
1908
+ var import_locale3 = require("date-fns/locale");
1909
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1910
+ function WorkspaceWebhooksPage({ onBack }) {
1911
+ const { webhookApi, agentApi } = useWorkspaceApi();
1912
+ const [subscriptions, setSubscriptions] = (0, import_react10.useState)([]);
1913
+ const [agents, setAgents] = (0, import_react10.useState)([]);
1914
+ const [loading, setLoading] = (0, import_react10.useState)(true);
1915
+ const [error, setError] = (0, import_react10.useState)("");
1916
+ const [deleteId, setDeleteId] = (0, import_react10.useState)(null);
1917
+ const [searchFilter, setSearchFilter] = (0, import_react10.useState)("");
1918
+ const [typeFilter, setTypeFilter] = (0, import_react10.useState)("all");
1919
+ const [showForm, setShowForm] = (0, import_react10.useState)(false);
1920
+ const [editingId, setEditingId] = (0, import_react10.useState)(null);
1921
+ const [saving, setSaving] = (0, import_react10.useState)(false);
1922
+ const [form, setForm] = (0, import_react10.useState)({
1923
+ name: "",
1924
+ targetUrl: "",
1925
+ secretKey: "x-api-key",
1926
+ secretValue: "",
1927
+ eventTypes: ["*"],
1928
+ active: true,
1929
+ isAgent: false,
1930
+ agentId: ""
1931
+ });
1932
+ const load = (0, import_react10.useCallback)(async () => {
1933
+ try {
1934
+ setLoading(true);
1935
+ const params = {};
1936
+ if (searchFilter.trim()) params.search = searchFilter.trim();
1937
+ if (typeFilter === "agent") params.isAgent = "true";
1938
+ else if (typeFilter === "standard") params.isAgent = "false";
1939
+ const [data, agentsData] = await Promise.all([
1940
+ webhookApi.list(params),
1941
+ agentApi.list()
1942
+ ]);
1943
+ setSubscriptions(data);
1944
+ setAgents(agentsData);
1945
+ } catch (e) {
1946
+ setError(e.message);
1947
+ } finally {
1948
+ setLoading(false);
1949
+ }
1950
+ }, [searchFilter, typeFilter, webhookApi, agentApi]);
1951
+ (0, import_react10.useEffect)(() => {
1952
+ const t = setTimeout(() => load(), 300);
1953
+ return () => clearTimeout(t);
1954
+ }, [load]);
1955
+ const resetForm = () => {
1956
+ setForm({
1957
+ name: "",
1958
+ targetUrl: "",
1959
+ secretKey: "x-api-key",
1960
+ secretValue: "",
1961
+ eventTypes: ["*"],
1962
+ active: true,
1963
+ isAgent: false,
1964
+ agentId: ""
1965
+ });
1966
+ setEditingId(null);
1967
+ setShowForm(false);
1968
+ };
1969
+ const openCreateForm = () => {
1970
+ resetForm();
1971
+ setShowForm(true);
1972
+ };
1973
+ const openEditForm = (sub) => {
1974
+ setForm({
1975
+ name: sub.name,
1976
+ targetUrl: sub.targetUrl,
1977
+ secretKey: sub.secretKey,
1978
+ secretValue: "",
1979
+ eventTypes: [...sub.eventTypes],
1980
+ active: sub.active,
1981
+ isAgent: sub.isAgent ?? false,
1982
+ agentId: sub.agentId ?? ""
1983
+ });
1984
+ setEditingId(sub._id);
1985
+ setShowForm(true);
1986
+ };
1987
+ const handleSubmit = async (e) => {
1988
+ e.preventDefault();
1989
+ if (!form.name.trim() || !form.targetUrl.trim()) return;
1990
+ try {
1991
+ setSaving(true);
1992
+ if (editingId) {
1993
+ await webhookApi.update(editingId, {
1994
+ name: form.name,
1995
+ targetUrl: form.targetUrl,
1996
+ secretKey: form.secretKey,
1997
+ eventTypes: form.eventTypes,
1998
+ active: form.active,
1999
+ isAgent: form.isAgent,
2000
+ ...form.isAgent && form.agentId ? { agentId: form.agentId } : {},
2001
+ ...form.secretValue.trim() ? { secretValue: form.secretValue } : {}
2002
+ });
2003
+ } else {
2004
+ await webhookApi.create({
2005
+ name: form.name,
2006
+ targetUrl: form.targetUrl,
2007
+ secretKey: form.secretKey,
2008
+ secretValue: form.secretValue,
2009
+ eventTypes: form.eventTypes,
2010
+ active: form.active,
2011
+ isAgent: form.isAgent,
2012
+ agentId: form.isAgent ? form.agentId : void 0
2013
+ });
2014
+ }
2015
+ resetForm();
2016
+ await load();
2017
+ } catch (e2) {
2018
+ setError(e2.message);
2019
+ } finally {
2020
+ setSaving(false);
2021
+ }
2022
+ };
2023
+ const toggleActive = async (sub) => {
2024
+ try {
2025
+ await webhookApi.update(sub._id, { active: !sub.active });
2026
+ setSubscriptions(
2027
+ (prev) => prev.map((s) => s._id === sub._id ? { ...s, active: !s.active } : s)
2028
+ );
2029
+ } catch (e) {
2030
+ setError(e.message);
2031
+ }
2032
+ };
2033
+ const confirmDelete = (id) => setDeleteId(id);
2034
+ const executeDelete = async () => {
2035
+ if (!deleteId) return;
2036
+ try {
2037
+ await webhookApi.delete(deleteId);
2038
+ setSubscriptions((prev) => prev.filter((s) => s._id !== deleteId));
2039
+ } catch (e) {
2040
+ setError(e.message);
2041
+ }
2042
+ };
2043
+ const toggleEvent = (event) => {
2044
+ setForm((prev) => {
2045
+ if (event === "*") {
2046
+ return { ...prev, eventTypes: ["*"] };
2047
+ }
2048
+ const without = prev.eventTypes.filter((e) => e !== "*" && e !== event);
2049
+ if (prev.eventTypes.includes(event)) {
2050
+ return { ...prev, eventTypes: without.length ? without : ["*"] };
2051
+ }
2052
+ return { ...prev, eventTypes: [...without, event] };
2053
+ });
2054
+ };
2055
+ if (loading && subscriptions.length === 0) {
2056
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.Loader2, { size: 32, className: "text-primary animate-spin" }) });
2057
+ }
2058
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex h-full flex-col", children: [
2059
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "bg-surface border-border flex shrink-0 items-center gap-4 border-b px-6 py-4", children: [
2060
+ onBack && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2061
+ "button",
2062
+ {
2063
+ type: "button",
2064
+ onClick: () => onBack(),
2065
+ className: "text-text-muted hover:text-text hover:bg-bg rounded-lg p-1.5 transition",
2066
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.ArrowLeft, { size: 20 })
2067
+ }
2068
+ ),
2069
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "bg-primary-light text-primary flex h-10 w-10 shrink-0 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.Webhook, { size: 24 }) }),
2070
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex-1", children: [
2071
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h1", { className: "text-text text-xl font-semibold", children: "Webhook Subscriptions" }),
2072
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-text-secondary text-sm", children: "G\xE9rer les souscriptions pour la distribution d'\xE9v\xE9nements" })
2073
+ ] }),
2074
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2075
+ "button",
2076
+ {
2077
+ type: "button",
2078
+ onClick: openCreateForm,
2079
+ 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",
2080
+ children: [
2081
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.Plus, { size: 16 }),
2082
+ " Nouvelle souscription"
2083
+ ]
2084
+ }
2085
+ )
2086
+ ] }),
2087
+ error && /* @__PURE__ */ (0, import_jsx_runtime7.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: [
2088
+ error,
2089
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { type: "button", onClick: () => setError(""), className: "ml-2 underline", children: "\xD7" })
2090
+ ] }),
2091
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2092
+ ConfirmModal,
2093
+ {
2094
+ open: !!deleteId,
2095
+ onClose: () => setDeleteId(null),
2096
+ onConfirm: executeDelete,
2097
+ title: "Supprimer ce webhook",
2098
+ description: "Voulez-vous vraiment supprimer ce webhook ?",
2099
+ danger: true
2100
+ }
2101
+ ),
2102
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex-1 overflow-y-auto p-6", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mx-auto max-w-4xl", children: [
2103
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mb-4 flex flex-wrap items-center gap-3", children: [
2104
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "relative", children: [
2105
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2106
+ import_lucide_react6.Search,
2107
+ {
2108
+ size: 16,
2109
+ className: "text-text-muted absolute top-1/2 left-3 -translate-y-1/2"
2110
+ }
2111
+ ),
2112
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2113
+ "input",
2114
+ {
2115
+ value: searchFilter,
2116
+ onChange: (e) => setSearchFilter(e.target.value),
2117
+ placeholder: "Rechercher par nom ou URL...",
2118
+ 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"
2119
+ }
2120
+ )
2121
+ ] }),
2122
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex items-center gap-1.5", children: [
2123
+ { key: "all", label: "Tous" },
2124
+ { key: "standard", label: "Standard" },
2125
+ { key: "agent", label: "Agent" }
2126
+ ].map(({ key, label }) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2127
+ "button",
2128
+ {
2129
+ type: "button",
2130
+ onClick: () => setTypeFilter(key),
2131
+ 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"}`,
2132
+ children: label
2133
+ },
2134
+ key
2135
+ )) }),
2136
+ !loading && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "text-text-muted text-xs", children: [
2137
+ subscriptions.length,
2138
+ " r\xE9sultat",
2139
+ subscriptions.length > 1 ? "s" : ""
2140
+ ] })
2141
+ ] }),
2142
+ showForm && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "bg-surface border-border mb-6 rounded-2xl border p-6 shadow-sm", children: [
2143
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mb-4 flex items-center justify-between", children: [
2144
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { className: "text-text text-base font-semibold", children: editingId ? "Modifier la souscription" : "Nouvelle souscription" }),
2145
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2146
+ "button",
2147
+ {
2148
+ type: "button",
2149
+ onClick: resetForm,
2150
+ className: "text-text-muted hover:text-text transition",
2151
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.X, { size: 20 })
2152
+ }
2153
+ )
2154
+ ] }),
2155
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
2156
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
2157
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
2158
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Nom" }),
2159
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2160
+ "input",
2161
+ {
2162
+ value: form.name,
2163
+ onChange: (e) => setForm({ ...form, name: e.target.value }),
2164
+ required: true,
2165
+ placeholder: "Garfield Production",
2166
+ 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"
2167
+ }
2168
+ )
2169
+ ] }),
2170
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
2171
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "URL cible" }),
2172
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2173
+ "input",
2174
+ {
2175
+ value: form.targetUrl,
2176
+ onChange: (e) => setForm({ ...form, targetUrl: e.target.value }),
2177
+ required: true,
2178
+ placeholder: "https://api.example.com/webhooks",
2179
+ 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"
2180
+ }
2181
+ )
2182
+ ] })
2183
+ ] }),
2184
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "grid grid-cols-2 gap-4", children: [
2185
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
2186
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Cl\xE9 du header d'authentification" }),
2187
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2188
+ "input",
2189
+ {
2190
+ value: form.secretKey,
2191
+ onChange: (e) => setForm({ ...form, secretKey: e.target.value }),
2192
+ required: true,
2193
+ placeholder: "x-api-key",
2194
+ 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"
2195
+ }
2196
+ )
2197
+ ] }),
2198
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
2199
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "text-text-secondary mb-1 block text-xs", children: [
2200
+ "Valeur du secret",
2201
+ editingId && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-text-muted ml-1", children: "(laisser vide pour ne pas modifier)" })
2202
+ ] }),
2203
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2204
+ "input",
2205
+ {
2206
+ value: form.secretValue,
2207
+ onChange: (e) => setForm({ ...form, secretValue: e.target.value }),
2208
+ required: !editingId,
2209
+ type: "password",
2210
+ placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022",
2211
+ 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"
2212
+ }
2213
+ )
2214
+ ] })
2215
+ ] }),
2216
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
2217
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-text-secondary mb-2 block text-xs", children: "\xC9v\xE9nements" }),
2218
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex flex-wrap gap-2", children: AVAILABLE_WEBHOOK_EVENTS.map((evt) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2219
+ "button",
2220
+ {
2221
+ type: "button",
2222
+ onClick: () => toggleEvent(evt),
2223
+ 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"}`,
2224
+ children: evt === "*" ? "Tous (*)" : evt
2225
+ },
2226
+ evt
2227
+ )) })
2228
+ ] }),
2229
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-3", children: [
2230
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2231
+ "button",
2232
+ {
2233
+ type: "button",
2234
+ onClick: () => setForm({ ...form, active: !form.active }),
2235
+ className: `relative h-5 w-10 rounded-full transition ${form.active ? "bg-primary" : "bg-border"}`,
2236
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2237
+ "span",
2238
+ {
2239
+ className: `absolute top-0.5 left-0.5 h-4 w-4 rounded-full bg-white shadow transition-transform ${form.active ? "translate-x-5" : ""}`
2240
+ }
2241
+ )
2242
+ }
2243
+ ),
2244
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-text text-sm", children: form.active ? "Active" : "Inactive" })
2245
+ ] }),
2246
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-3", children: [
2247
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2248
+ "button",
2249
+ {
2250
+ type: "button",
2251
+ onClick: () => setForm({ ...form, isAgent: !form.isAgent }),
2252
+ className: `relative h-5 w-10 rounded-full transition ${form.isAgent ? "bg-primary" : "bg-border"}`,
2253
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2254
+ "span",
2255
+ {
2256
+ className: `absolute top-0.5 left-0.5 h-4 w-4 rounded-full bg-white shadow transition-transform ${form.isAgent ? "translate-x-5" : ""}`
2257
+ }
2258
+ )
2259
+ }
2260
+ ),
2261
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-text text-sm", children: form.isAgent ? "Webhook Agent" : "Webhook standard" })
2262
+ ] }),
2263
+ form.isAgent && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
2264
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("label", { className: "text-text-secondary mb-1 block text-xs", children: "Agent" }),
2265
+ agents.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-warning py-1 text-xs", children: "Aucun agent disponible. Cr\xE9ez d'abord un agent dans la section Agents." }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2266
+ "select",
2267
+ {
2268
+ value: form.agentId,
2269
+ onChange: (e) => setForm({ ...form, agentId: e.target.value }),
2270
+ required: true,
2271
+ 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",
2272
+ children: [
2273
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("option", { value: "", children: "-- S\xE9lectionner un agent --" }),
2274
+ agents.map((a) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("option", { value: a.agentId, children: [
2275
+ a.agentName,
2276
+ " (",
2277
+ a.agentId,
2278
+ ")"
2279
+ ] }, a._id))
2280
+ ]
2281
+ }
2282
+ )
2283
+ ] }),
2284
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex gap-3 pt-1", children: [
2285
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2286
+ "button",
2287
+ {
2288
+ type: "submit",
2289
+ disabled: saving,
2290
+ 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",
2291
+ children: [
2292
+ saving && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.Loader2, { size: 16, className: "animate-spin" }),
2293
+ editingId ? "Enregistrer" : "Cr\xE9er"
2294
+ ]
2295
+ }
2296
+ ),
2297
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2298
+ "button",
2299
+ {
2300
+ type: "button",
2301
+ onClick: resetForm,
2302
+ className: "bg-bg text-text hover:bg-border-light rounded-xl px-5 py-2 text-sm font-medium transition",
2303
+ children: "Annuler"
2304
+ }
2305
+ )
2306
+ ] })
2307
+ ] })
2308
+ ] }),
2309
+ subscriptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "text-text-muted py-20 text-center", children: [
2310
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.Webhook, { size: 40, className: "mx-auto mb-3 opacity-30" }),
2311
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-sm", children: "Aucune souscription webhook." }),
2312
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "text-text-muted mt-1 text-xs", children: "Cr\xE9ez une souscription pour recevoir les \xE9v\xE9nements workspace." })
2313
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "space-y-3", children: subscriptions.map((sub) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2314
+ "div",
2315
+ {
2316
+ className: "bg-surface border-border group rounded-xl border px-5 py-4 transition hover:shadow-md",
2317
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-start justify-between gap-4", children: [
2318
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "min-w-0 flex-1", children: [
2319
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mb-1 flex items-center gap-2", children: [
2320
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { className: "text-text truncate text-sm font-semibold", children: sub.name }),
2321
+ sub.active ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("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: [
2322
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.CircleCheck, { size: 12 }),
2323
+ "Active"
2324
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("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: [
2325
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.CircleX, { size: 12 }),
2326
+ "Inactive"
2327
+ ] }),
2328
+ sub.isAgent && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("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" })
2329
+ ] }),
2330
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "text-text-muted mb-2 flex items-center gap-1.5 text-xs", children: [
2331
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.ExternalLink, { size: 12, className: "shrink-0" }),
2332
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "truncate", children: sub.targetUrl })
2333
+ ] }),
2334
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex flex-wrap gap-1.5", children: sub.eventTypes.map((evt) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2335
+ "span",
2336
+ {
2337
+ className: "bg-bg text-text-secondary rounded-md px-2 py-0.5 text-xs",
2338
+ children: evt === "*" ? "Tous" : evt
2339
+ },
2340
+ evt
2341
+ )) }),
2342
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { className: "text-text-muted mt-2 text-xs", children: [
2343
+ "Authentification : ",
2344
+ sub.secretKey,
2345
+ sub.isAgent && sub.agentId && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
2346
+ " \u2022 Agent: ",
2347
+ sub.agentId
2348
+ ] }),
2349
+ " ",
2350
+ "\u2022",
2351
+ " ",
2352
+ (0, import_date_fns3.formatDistanceToNow)(new Date(sub.createdAt), {
2353
+ addSuffix: true,
2354
+ locale: import_locale3.fr
2355
+ })
2356
+ ] })
2357
+ ] }),
2358
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex shrink-0 items-center gap-1", children: [
2359
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2360
+ "button",
2361
+ {
2362
+ type: "button",
2363
+ onClick: () => toggleActive(sub),
2364
+ className: `rounded-lg p-2 transition ${sub.active ? "text-success hover:bg-success-light" : "text-text-muted hover:bg-bg"}`,
2365
+ title: sub.active ? "D\xE9sactiver" : "Activer",
2366
+ children: sub.active ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.CircleCheck, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.CircleX, { size: 16 })
2367
+ }
2368
+ ),
2369
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2370
+ "button",
2371
+ {
2372
+ type: "button",
2373
+ onClick: () => openEditForm(sub),
2374
+ className: "text-text-muted hover:text-primary hover:bg-primary-light rounded-lg p-2 transition",
2375
+ title: "Modifier",
2376
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.Pencil, { size: 16 })
2377
+ }
2378
+ ),
2379
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2380
+ "button",
2381
+ {
2382
+ type: "button",
2383
+ onClick: () => confirmDelete(sub._id),
2384
+ className: "text-text-muted hover:text-danger hover:bg-danger-light rounded-lg p-2 opacity-0 transition group-hover:opacity-100",
2385
+ title: "Supprimer",
2386
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react6.Trash2, { size: 16 })
2387
+ }
2388
+ )
2389
+ ] })
2390
+ ] })
2391
+ },
2392
+ sub._id
2393
+ )) })
2394
+ ] }) })
2395
+ ] });
2396
+ }
2397
+ // Annotate the CommonJS export names for ESM import in node:
2398
+ 0 && (module.exports = {
2399
+ AVAILABLE_WEBHOOK_EVENTS,
2400
+ WorkspaceAgentsPage,
2401
+ WorkspaceTypesPage,
2402
+ WorkspaceWebhooksPage
2403
+ });
19
2404
  //# sourceMappingURL=admin.js.map