@ramses1er/cbk-toolkit 2.0.0 → 2.1.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/client.js CHANGED
@@ -1,17 +1,260 @@
1
1
  // packages/ui/src/layouts/side-nav.tsx
2
2
  import {
3
- useState,
4
- useEffect,
3
+ useState as useState2,
4
+ useEffect as useEffect3,
5
5
  createElement,
6
6
  isValidElement
7
7
  } from "react";
8
+
9
+ // packages/ui/src/modals/modal.tsx
10
+ import { X } from "lucide-react";
11
+ import { useEffect } from "react";
12
+ import { createPortal } from "react-dom";
8
13
  import { jsx, jsxs } from "react/jsx-runtime";
14
+ function Modal({ title, isOpen, onClose, children }) {
15
+ const requestClose = () => {
16
+ onClose();
17
+ };
18
+ useEffect(() => {
19
+ if (isOpen) {
20
+ document.body.style.overflow = "hidden";
21
+ }
22
+ return () => {
23
+ document.body.style.overflow = "";
24
+ };
25
+ }, [isOpen]);
26
+ useEffect(() => {
27
+ function handleEsc(e) {
28
+ if (e.key === "Escape") {
29
+ onClose();
30
+ }
31
+ }
32
+ if (isOpen) {
33
+ document.addEventListener("keydown", handleEsc);
34
+ }
35
+ return () => {
36
+ document.removeEventListener("keydown", handleEsc);
37
+ };
38
+ }, [isOpen, onClose]);
39
+ if (!isOpen) return null;
40
+ const content = typeof children === "function" ? children(requestClose) : children;
41
+ return createPortal(
42
+ /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
43
+ /* @__PURE__ */ jsx(
44
+ "div",
45
+ {
46
+ className: "absolute inset-0 bg-black/20 backdrop-blur-sm",
47
+ onClick: onClose
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsxs("div", { className: "relative bg-white rounded-lg shadow-lg w-full max-w-2xl mx-4 p-6 flex flex-col max-h-[85vh]", children: [
51
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4 border-b-2 border-gray-400 pb-3 sticky top-0 bg-white z-10", children: [
52
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: title }),
53
+ /* @__PURE__ */ jsx(
54
+ "button",
55
+ {
56
+ onClick: onClose,
57
+ className: "text-gray-500 hover:text-gray-700 text-xl cursor-pointer",
58
+ children: /* @__PURE__ */ jsx(X, {})
59
+ }
60
+ )
61
+ ] }),
62
+ /* @__PURE__ */ jsx("div", { className: "overflow-y-auto flex-1 min-h-0", children: content })
63
+ ] })
64
+ ] }),
65
+ document.body
66
+ );
67
+ }
68
+
69
+ // packages/ui/src/feedback/confirmation.tsx
70
+ import { useCallback, useEffect as useEffect2, useRef, useState } from "react";
71
+ import { CircleQuestionMark } from "lucide-react";
72
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
73
+ var ERROR_COUNTDOWN = 10;
74
+ function Confirmation({
75
+ question,
76
+ messageYes,
77
+ messageNo,
78
+ requestClose,
79
+ onAction,
80
+ isProcessing,
81
+ error,
82
+ resetRequest
83
+ }) {
84
+ const [countdown, setCountdown] = useState(0);
85
+ const countdownRef = useRef(null);
86
+ const resetRequestRef = useRef(resetRequest);
87
+ useEffect2(() => {
88
+ resetRequestRef.current = resetRequest;
89
+ }, [resetRequest]);
90
+ const clearCountdown = useCallback(() => {
91
+ if (countdownRef.current !== null) {
92
+ window.clearInterval(countdownRef.current);
93
+ countdownRef.current = null;
94
+ }
95
+ }, []);
96
+ const resetConfirmationState = useCallback(() => {
97
+ clearCountdown();
98
+ setCountdown(0);
99
+ }, [clearCountdown]);
100
+ const handleResetRequest = useCallback(() => {
101
+ resetConfirmationState();
102
+ resetRequest();
103
+ }, [resetConfirmationState, resetRequest]);
104
+ const handleRequestClose = useCallback(() => {
105
+ resetConfirmationState();
106
+ resetRequest();
107
+ requestClose();
108
+ }, [requestClose, resetConfirmationState, resetRequest]);
109
+ const handleAction = useCallback(async () => {
110
+ try {
111
+ await onAction();
112
+ handleRequestClose();
113
+ } catch {
114
+ }
115
+ }, [handleRequestClose, onAction]);
116
+ useEffect2(() => {
117
+ clearCountdown();
118
+ if (!error) {
119
+ const resetTimeoutId = window.setTimeout(() => {
120
+ setCountdown(0);
121
+ }, 0);
122
+ return () => {
123
+ window.clearTimeout(resetTimeoutId);
124
+ };
125
+ }
126
+ const startCountdownTimeoutId = window.setTimeout(() => {
127
+ setCountdown(ERROR_COUNTDOWN);
128
+ countdownRef.current = window.setInterval(() => {
129
+ setCountdown((previousCountdown) => {
130
+ if (previousCountdown <= 1) {
131
+ clearCountdown();
132
+ window.setTimeout(() => {
133
+ resetRequestRef.current();
134
+ }, 0);
135
+ return 0;
136
+ }
137
+ return previousCountdown - 1;
138
+ });
139
+ }, 1e3);
140
+ }, 0);
141
+ return () => {
142
+ window.clearTimeout(startCountdownTimeoutId);
143
+ clearCountdown();
144
+ };
145
+ }, [clearCountdown, error]);
146
+ useEffect2(() => {
147
+ return () => {
148
+ clearCountdown();
149
+ resetRequestRef.current();
150
+ };
151
+ }, [clearCountdown]);
152
+ const isErrorVisible = Boolean(error) && countdown > 0;
153
+ if (!question) return null;
154
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center gap-3 z-40", children: [
155
+ /* @__PURE__ */ jsx2(CircleQuestionMark, { className: "text-red-500 text-5xl" }),
156
+ /* @__PURE__ */ jsx2("h2", { className: "text-xl font-bold text-red-500", children: "Confirmation requise" }),
157
+ /* @__PURE__ */ jsx2("p", { className: "mt-4 text-md leading-relaxed text-gray-700", children: question }),
158
+ /* @__PURE__ */ jsxs2("div", { className: "mt-6 flex w-full gap-3", children: [
159
+ /* @__PURE__ */ jsx2(
160
+ "button",
161
+ {
162
+ type: "button",
163
+ onClick: handleRequestClose,
164
+ disabled: isProcessing,
165
+ className: "w-full rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition hover:cursor-pointer hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-white",
166
+ children: messageNo
167
+ }
168
+ ),
169
+ /* @__PURE__ */ jsx2(
170
+ "button",
171
+ {
172
+ type: "button",
173
+ onClick: handleAction,
174
+ disabled: isProcessing,
175
+ className: "w-full rounded-lg bg-red-400 px-4 py-2 text-sm font-semibold text-white transition hover:cursor-pointer hover:bg-red-500 active:scale-95 disabled:opacity-50 disabled:hover:bg-red-500",
176
+ children: isProcessing ? "operation en cours" : messageYes
177
+ }
178
+ )
179
+ ] }),
180
+ error && isErrorVisible && /* @__PURE__ */ jsxs2("div", { className: "mt-4 w-full rounded-lg border border-red-200 bg-red-50 p-4 text-left text-sm text-red-700", children: [
181
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-3", children: [
182
+ /* @__PURE__ */ jsxs2("div", { children: [
183
+ /* @__PURE__ */ jsx2("p", { className: "font-semibold", children: "Erreur" }),
184
+ /* @__PURE__ */ jsx2("p", { className: "mt-1 text-sm", children: error.message })
185
+ ] }),
186
+ /* @__PURE__ */ jsx2(
187
+ "button",
188
+ {
189
+ type: "button",
190
+ onClick: handleResetRequest,
191
+ className: "rounded-full bg-red-100 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-200",
192
+ children: "Fermer"
193
+ }
194
+ )
195
+ ] }),
196
+ /* @__PURE__ */ jsxs2("p", { className: "mt-3 text-xs text-red-600", children: [
197
+ "Ce message disparait dans ",
198
+ countdown,
199
+ "s..."
200
+ ] })
201
+ ] })
202
+ ] });
203
+ }
204
+
205
+ // packages/utils/src/fetcher.ts
206
+ var ApiError = class extends Error {
207
+ status;
208
+ code;
209
+ data;
210
+ constructor(params) {
211
+ super(params.message);
212
+ this.status = params.status;
213
+ this.code = params.code;
214
+ this.data = params.data;
215
+ }
216
+ };
217
+ async function fetcher(url, options = {}) {
218
+ const { headers, skipJsonParsing = false, ...rest } = options;
219
+ const response = await fetch(url, {
220
+ credentials: "include",
221
+ headers: {
222
+ "Content-Type": "application/json",
223
+ ...headers
224
+ },
225
+ ...rest
226
+ });
227
+ let responseData = null;
228
+ const contentType = response.headers.get("content-type");
229
+ if (!skipJsonParsing && contentType?.includes("application/json")) {
230
+ responseData = await response.json();
231
+ }
232
+ if (!response.ok) {
233
+ const backendError = responseData;
234
+ throw new ApiError({
235
+ message: backendError?.message || "Une erreur est survenue",
236
+ status: backendError?.status || response.status,
237
+ code: backendError?.code,
238
+ data: responseData
239
+ });
240
+ }
241
+ return responseData;
242
+ }
243
+
244
+ // packages/ui/src/layouts/side-nav.tsx
245
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
9
246
  function isActive(href, currentPath) {
10
247
  return currentPath === href || currentPath.startsWith(href + "/");
11
248
  }
12
249
  function isExactActive(href, currentPath) {
13
250
  return currentPath === href;
14
251
  }
252
+ function renderItemIcon(icon) {
253
+ if (!icon) return null;
254
+ if (typeof icon === "string") return /* @__PURE__ */ jsx3("span", { className: "text-lg", children: icon });
255
+ if (isValidElement(icon)) return icon;
256
+ return createElement(icon, { size: 20 });
257
+ }
15
258
  function SideNav({
16
259
  children,
17
260
  title,
@@ -23,21 +266,21 @@ function SideNav({
23
266
  }) {
24
267
  const activeMain = mainItems.find((item) => isActive(item.href, currentPath));
25
268
  const headerTitle = title ?? activeMain?.title ?? activeMain?.label;
26
- const [expandedSection, setExpandedSection] = useState(
269
+ const [expandedSection, setExpandedSection] = useState2(
27
270
  activeMain?.id ?? null
28
271
  );
29
272
  const toggleSection = (id) => {
30
273
  setExpandedSection((prev) => prev === id ? null : id);
31
274
  };
32
- const [isMobileOpen, setIsMobileOpen] = useState(false);
33
- useEffect(() => {
275
+ const [isMobileOpen, setIsMobileOpen] = useState2(false);
276
+ useEffect3(() => {
34
277
  const handleEscape = (e) => {
35
278
  if (e.key === "Escape") setIsMobileOpen(false);
36
279
  };
37
280
  document.addEventListener("keydown", handleEscape);
38
281
  return () => document.removeEventListener("keydown", handleEscape);
39
282
  }, []);
40
- useEffect(() => {
283
+ useEffect3(() => {
41
284
  if (isMobileOpen) {
42
285
  document.body.style.overflow = "hidden";
43
286
  } else {
@@ -48,14 +291,45 @@ function SideNav({
48
291
  };
49
292
  }, [isMobileOpen]);
50
293
  const closeMobileNav = () => setIsMobileOpen(false);
51
- return /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen", children: [
52
- /* @__PURE__ */ jsxs(
294
+ const [confirmingItem, setConfirmingItem] = useState2(null);
295
+ const [pending, setPending] = useState2(false);
296
+ const [error, setError] = useState2(null);
297
+ const openConfirm = (item) => {
298
+ setError(null);
299
+ setConfirmingItem(item);
300
+ closeMobileNav();
301
+ };
302
+ const closeConfirm = () => {
303
+ if (pending) return;
304
+ setConfirmingItem(null);
305
+ setError(null);
306
+ };
307
+ const handleConfirmAction = async () => {
308
+ if (!confirmingItem || !("confirm" in confirmingItem)) return;
309
+ setPending(true);
310
+ setError(null);
311
+ try {
312
+ await confirmingItem.confirm.onAction();
313
+ } catch (e) {
314
+ setError(
315
+ e instanceof ApiError ? e : new ApiError({
316
+ message: e instanceof Error ? e.message : "Une erreur est survenue",
317
+ status: 500
318
+ })
319
+ );
320
+ throw e;
321
+ } finally {
322
+ setPending(false);
323
+ }
324
+ };
325
+ return /* @__PURE__ */ jsxs3("div", { className: "flex min-h-screen", children: [
326
+ /* @__PURE__ */ jsxs3(
53
327
  "aside",
54
328
  {
55
329
  className: `w-64 bg-white border-r border-gray-200 flex flex-col fixed h-full z-50 transition-transform duration-300 ease-in-out md:translate-x-0 ${isMobileOpen ? "translate-x-0" : "-translate-x-full"}`,
56
330
  children: [
57
- brand && /* @__PURE__ */ jsx("div", { className: "p-[6.5px] border-b border-gray-100", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-row gap-2 items-center", children: [
58
- brand.logo && (typeof brand.logo === "string" ? /* @__PURE__ */ jsx(
331
+ brand && /* @__PURE__ */ jsx3("div", { className: "p-[6.5px] border-b border-gray-100", children: /* @__PURE__ */ jsxs3("div", { className: "flex flex-row gap-2 items-center", children: [
332
+ brand.logo && (typeof brand.logo === "string" ? /* @__PURE__ */ jsx3(
59
333
  "img",
60
334
  {
61
335
  src: brand.logo,
@@ -63,22 +337,22 @@ function SideNav({
63
337
  className: "w-12.5 h-12.5"
64
338
  }
65
339
  ) : brand.logo),
66
- brand.name && /* @__PURE__ */ jsx("p", { className: "text-gray-500 text-base font-semibold", children: brand.name })
340
+ brand.name && /* @__PURE__ */ jsx3("p", { className: "text-gray-500 text-base font-semibold", children: brand.name })
67
341
  ] }) }),
68
- /* @__PURE__ */ jsx("nav", { className: "flex-1 p-4 overflow-y-auto", children: /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: mainItems.map((item) => {
342
+ /* @__PURE__ */ jsx3("nav", { className: "flex-1 p-4 overflow-y-auto", children: /* @__PURE__ */ jsx3("ul", { className: "space-y-1", children: mainItems.map((item) => {
69
343
  const active = isActive(item.href, currentPath);
70
344
  const isExpanded = expandedSection === item.id;
71
345
  const hasSubItems = item.subItems && item.subItems.length > 0;
72
- return /* @__PURE__ */ jsxs("li", { children: [
73
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
74
- /* @__PURE__ */ jsxs(
346
+ return /* @__PURE__ */ jsxs3("li", { children: [
347
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
348
+ /* @__PURE__ */ jsxs3(
75
349
  "a",
76
350
  {
77
351
  href: item.href,
78
352
  onClick: closeMobileNav,
79
353
  className: `flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors flex-1 ${active ? "bg-gray-200 text-black" : "text-gray-500 hover:bg-gray-100"}`,
80
354
  children: [
81
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx("span", { className: "text-lg", children: item.icon }) : isValidElement(item.icon) ? item.icon : createElement(
355
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx3("span", { className: "text-lg", children: item.icon }) : isValidElement(item.icon) ? item.icon : createElement(
82
356
  item.icon,
83
357
  { size: 20 }
84
358
  )),
@@ -86,27 +360,27 @@ function SideNav({
86
360
  ]
87
361
  }
88
362
  ),
89
- active && hasSubItems && /* @__PURE__ */ jsx(
363
+ active && hasSubItems && /* @__PURE__ */ jsx3(
90
364
  "button",
91
365
  {
92
366
  onClick: () => toggleSection(item.id),
93
367
  className: `p-1.5 rounded-lg transition-colors ${active ? "text-black hover:bg-gray-100" : "text-gray-500 hover:bg-gray-100"}`,
94
368
  title: isExpanded ? "R\xE9duire" : "D\xE9velopper",
95
- children: toggleIcon ? /* @__PURE__ */ jsx(
369
+ children: toggleIcon ? /* @__PURE__ */ jsx3(
96
370
  "span",
97
371
  {
98
372
  className: `inline-block transition-transform duration-200 ${isExpanded ? "rotate-0" : "-rotate-90"}`,
99
373
  children: toggleIcon
100
374
  }
101
- ) : /* @__PURE__ */ jsx("span", { className: "text-xs", children: isExpanded ? "\u25BC" : "\u25B6" })
375
+ ) : /* @__PURE__ */ jsx3("span", { className: "text-xs", children: isExpanded ? "\u25BC" : "\u25B6" })
102
376
  }
103
377
  )
104
378
  ] }),
105
- active && hasSubItems && /* @__PURE__ */ jsx(
379
+ active && hasSubItems && /* @__PURE__ */ jsx3(
106
380
  "div",
107
381
  {
108
382
  className: `grid transition-all duration-300 ease-in-out ${isExpanded ? "grid-rows-[1fr]" : "grid-rows-[0fr]"}`,
109
- children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx("ul", { className: "ml-4 mt-1 space-y-1 pl-2", children: item.subItems.map((sub) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
383
+ children: /* @__PURE__ */ jsx3("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx3("ul", { className: "ml-4 mt-1 space-y-1 pl-2", children: item.subItems.map((sub) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(
110
384
  "a",
111
385
  {
112
386
  href: sub.href,
@@ -119,25 +393,61 @@ function SideNav({
119
393
  )
120
394
  ] }, item.id);
121
395
  }) }) }),
122
- footer && footer.length > 0 && /* @__PURE__ */ jsx("div", { className: "p-4 border-t border-gray-100 space-y-1", children: footer.map((item) => /* @__PURE__ */ jsxs(
123
- "a",
124
- {
125
- href: item.href,
126
- onClick: closeMobileNav,
127
- className: "flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-100 transition-colors",
128
- children: [
129
- typeof item.icon === "string" ? /* @__PURE__ */ jsx("span", { className: "text-lg", children: item.icon }) : isValidElement(item.icon) ? item.icon : createElement(item.icon, {
130
- size: 20
131
- }),
132
- item.label
133
- ]
134
- },
135
- item.label
136
- )) })
396
+ footer && footer.length > 0 && /* @__PURE__ */ jsx3("div", { className: "p-4 border-t border-gray-100 space-y-1", children: footer.map(
397
+ (item) => "confirm" in item ? /* @__PURE__ */ jsxs3(
398
+ "button",
399
+ {
400
+ type: "button",
401
+ onClick: () => openConfirm(item),
402
+ className: "flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-100 transition-colors bg-transparent cursor-pointer",
403
+ children: [
404
+ renderItemIcon(item.icon),
405
+ item.label
406
+ ]
407
+ },
408
+ item.label
409
+ ) : /* @__PURE__ */ jsxs3(
410
+ "a",
411
+ {
412
+ href: item.href,
413
+ onClick: closeMobileNav,
414
+ className: "flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium text-gray-700 hover:bg-gray-100 transition-colors",
415
+ children: [
416
+ renderItemIcon(item.icon),
417
+ item.label
418
+ ]
419
+ },
420
+ item.label
421
+ )
422
+ ) })
137
423
  ]
138
424
  }
139
425
  ),
140
- isMobileOpen && /* @__PURE__ */ jsx(
426
+ confirmingItem && "confirm" in confirmingItem && /* @__PURE__ */ jsx3(
427
+ Modal,
428
+ {
429
+ title: confirmingItem.confirm.title ?? confirmingItem.label,
430
+ isOpen: true,
431
+ onClose: closeConfirm,
432
+ children: (requestClose) => /* @__PURE__ */ jsx3(
433
+ Confirmation,
434
+ {
435
+ question: confirmingItem.confirm.question,
436
+ messageYes: confirmingItem.confirm.messageYes,
437
+ messageNo: confirmingItem.confirm.messageNo,
438
+ requestClose: () => {
439
+ setConfirmingItem(null);
440
+ setError(null);
441
+ },
442
+ onAction: handleConfirmAction,
443
+ isProcessing: pending,
444
+ error,
445
+ resetRequest: () => setError(null)
446
+ }
447
+ )
448
+ }
449
+ ),
450
+ isMobileOpen && /* @__PURE__ */ jsx3(
141
451
  "div",
142
452
  {
143
453
  className: "fixed inset-0 z-40 bg-black/50 md:hidden",
@@ -145,17 +455,17 @@ function SideNav({
145
455
  "aria-hidden": "true"
146
456
  }
147
457
  ),
148
- /* @__PURE__ */ jsxs("div", { className: "flex-1 ml-64 max-md:ml-0 min-w-0", children: [
149
- headerTitle && /* @__PURE__ */ jsxs("header", { className: "h-16 bg-white border-b border-gray-200 flex items-center justify-between px-6 fixed top-0 right-0 left-64 max-md:left-0 z-20", children: [
150
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
151
- /* @__PURE__ */ jsx(
458
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 ml-64 max-md:ml-0 min-w-0", children: [
459
+ headerTitle && /* @__PURE__ */ jsxs3("header", { className: "h-16 bg-white border-b border-gray-200 flex items-center justify-between px-6 fixed top-0 right-0 left-64 max-md:left-0 z-20", children: [
460
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
461
+ /* @__PURE__ */ jsx3(
152
462
  "button",
153
463
  {
154
464
  className: "md:hidden p-2 -ml-2 rounded-lg hover:bg-gray-100 transition-colors",
155
465
  onClick: () => setIsMobileOpen(!isMobileOpen),
156
466
  "aria-label": isMobileOpen ? "Fermer le menu" : "Menu",
157
467
  "aria-expanded": isMobileOpen,
158
- children: isMobileOpen ? /* @__PURE__ */ jsx(
468
+ children: isMobileOpen ? /* @__PURE__ */ jsx3(
159
469
  "svg",
160
470
  {
161
471
  className: "w-5 h-5",
@@ -163,7 +473,7 @@ function SideNav({
163
473
  viewBox: "0 0 24 24",
164
474
  stroke: "currentColor",
165
475
  strokeWidth: 2,
166
- children: /* @__PURE__ */ jsx(
476
+ children: /* @__PURE__ */ jsx3(
167
477
  "path",
168
478
  {
169
479
  strokeLinecap: "round",
@@ -172,7 +482,7 @@ function SideNav({
172
482
  }
173
483
  )
174
484
  }
175
- ) : /* @__PURE__ */ jsx(
485
+ ) : /* @__PURE__ */ jsx3(
176
486
  "svg",
177
487
  {
178
488
  className: "w-5 h-5",
@@ -180,7 +490,7 @@ function SideNav({
180
490
  viewBox: "0 0 24 24",
181
491
  stroke: "currentColor",
182
492
  strokeWidth: 2,
183
- children: /* @__PURE__ */ jsx(
493
+ children: /* @__PURE__ */ jsx3(
184
494
  "path",
185
495
  {
186
496
  strokeLinecap: "round",
@@ -192,18 +502,18 @@ function SideNav({
192
502
  )
193
503
  }
194
504
  ),
195
- /* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-500", children: headerTitle })
505
+ /* @__PURE__ */ jsx3("h1", { className: "text-base font-semibold text-gray-500", children: headerTitle })
196
506
  ] }),
197
- brand?.tagline && /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500 max-md:hidden", children: brand.tagline })
507
+ brand?.tagline && /* @__PURE__ */ jsx3("span", { className: "text-sm text-gray-500 max-md:hidden", children: brand.tagline })
198
508
  ] }),
199
- !headerTitle && /* @__PURE__ */ jsx(
509
+ !headerTitle && /* @__PURE__ */ jsx3(
200
510
  "button",
201
511
  {
202
512
  className: "md:hidden fixed top-4 left-4 z-10 p-2 rounded-lg bg-white border border-gray-200 shadow-sm hover:bg-gray-100 transition-colors",
203
513
  onClick: () => setIsMobileOpen(!isMobileOpen),
204
514
  "aria-label": isMobileOpen ? "Fermer le menu" : "Menu",
205
515
  "aria-expanded": isMobileOpen,
206
- children: isMobileOpen ? /* @__PURE__ */ jsx(
516
+ children: isMobileOpen ? /* @__PURE__ */ jsx3(
207
517
  "svg",
208
518
  {
209
519
  className: "w-5 h-5",
@@ -211,7 +521,7 @@ function SideNav({
211
521
  viewBox: "0 0 24 24",
212
522
  stroke: "currentColor",
213
523
  strokeWidth: 2,
214
- children: /* @__PURE__ */ jsx(
524
+ children: /* @__PURE__ */ jsx3(
215
525
  "path",
216
526
  {
217
527
  strokeLinecap: "round",
@@ -220,7 +530,7 @@ function SideNav({
220
530
  }
221
531
  )
222
532
  }
223
- ) : /* @__PURE__ */ jsx(
533
+ ) : /* @__PURE__ */ jsx3(
224
534
  "svg",
225
535
  {
226
536
  className: "w-5 h-5",
@@ -228,7 +538,7 @@ function SideNav({
228
538
  viewBox: "0 0 24 24",
229
539
  stroke: "currentColor",
230
540
  strokeWidth: 2,
231
- children: /* @__PURE__ */ jsx(
541
+ children: /* @__PURE__ */ jsx3(
232
542
  "path",
233
543
  {
234
544
  strokeLinecap: "round",
@@ -240,7 +550,7 @@ function SideNav({
240
550
  )
241
551
  }
242
552
  ),
243
- /* @__PURE__ */ jsx(
553
+ /* @__PURE__ */ jsx3(
244
554
  "main",
245
555
  {
246
556
  className: (headerTitle ? "p-6 pt-20" : "p-6") + " overflow-x-auto",
@@ -252,46 +562,46 @@ function SideNav({
252
562
  }
253
563
 
254
564
  // packages/ui/src/config/create-side-nav.tsx
255
- import { jsx as jsx2 } from "react/jsx-runtime";
565
+ import { jsx as jsx4 } from "react/jsx-runtime";
256
566
  function createSideNav(config) {
257
567
  return function ConfiguredSideNav({
258
568
  children,
259
569
  currentPath
260
570
  }) {
261
- return /* @__PURE__ */ jsx2(SideNav, { ...config, currentPath, children });
571
+ return /* @__PURE__ */ jsx4(SideNav, { ...config, currentPath, children });
262
572
  };
263
573
  }
264
574
 
265
575
  // packages/ui/src/table/table.tsx
266
576
  import { Printer, ChevronLeft, ChevronRight } from "lucide-react";
267
577
  import clsx from "clsx";
268
- import { useState as useState3, useMemo, useCallback } from "react";
578
+ import { useState as useState4, useMemo, useCallback as useCallback2 } from "react";
269
579
 
270
580
  // packages/ui/src/feedback/error-display.tsx
271
- import { X } from "lucide-react";
272
- import { useState as useState2 } from "react";
273
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
581
+ import { X as X2 } from "lucide-react";
582
+ import { useState as useState3 } from "react";
583
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
274
584
  function ErrorDisplay({ error }) {
275
- const [dismissed, setDismissed] = useState2(false);
585
+ const [dismissed, setDismissed] = useState3(false);
276
586
  if (!error || dismissed) return null;
277
- return /* @__PURE__ */ jsx3("div", { className: "w-full overflow-x-auto", children: /* @__PURE__ */ jsx3("div", { className: "rounded-lg shadow-md border border-red-200 bg-red-50", children: /* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-4 px-4 py-4", children: [
278
- /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-1", children: [
279
- /* @__PURE__ */ jsx3("p", { className: "text-red-700 font-medium", children: "Erreur" }),
280
- /* @__PURE__ */ jsx3("p", { className: "text-red-600 text-sm", children: error.message })
587
+ return /* @__PURE__ */ jsx5("div", { className: "w-full overflow-x-auto", children: /* @__PURE__ */ jsx5("div", { className: "rounded-lg shadow-md border border-red-200 bg-red-50", children: /* @__PURE__ */ jsxs4("div", { className: "flex items-start justify-between gap-4 px-4 py-4", children: [
588
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
589
+ /* @__PURE__ */ jsx5("p", { className: "text-red-700 font-medium", children: "Erreur" }),
590
+ /* @__PURE__ */ jsx5("p", { className: "text-red-600 text-sm", children: error.message })
281
591
  ] }),
282
- /* @__PURE__ */ jsx3(
592
+ /* @__PURE__ */ jsx5(
283
593
  "button",
284
594
  {
285
595
  onClick: () => setDismissed(true),
286
596
  className: "text-red-400 hover:text-red-600 shrink-0 cursor-pointer",
287
- children: /* @__PURE__ */ jsx3(X, { className: "w-5 h-5" })
597
+ children: /* @__PURE__ */ jsx5(X2, { className: "w-5 h-5" })
288
598
  }
289
599
  )
290
600
  ] }) }) });
291
601
  }
292
602
 
293
603
  // packages/ui/src/table/table.tsx
294
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
604
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
295
605
  var VISIBLE_PAGES = 5;
296
606
  function Table({
297
607
  data,
@@ -304,9 +614,9 @@ function Table({
304
614
  pageSizeOptions = [5, 10, 20],
305
615
  showPagination = true
306
616
  }) {
307
- const [tooltip, setTooltip] = useState3(null);
308
- const [page, setPage] = useState3(1);
309
- const [perPage, setPerPage] = useState3(pageSize);
617
+ const [tooltip, setTooltip] = useState4(null);
618
+ const [page, setPage] = useState4(1);
619
+ const [perPage, setPerPage] = useState4(pageSize);
310
620
  const totalPages = useMemo(
311
621
  () => Math.max(1, Math.ceil(data.length / perPage)),
312
622
  [data.length, perPage]
@@ -335,7 +645,7 @@ function Table({
335
645
  }
336
646
  return range;
337
647
  }, [safePage, totalPages]);
338
- const handlePageSizeChange = useCallback((value) => {
648
+ const handlePageSizeChange = useCallback2((value) => {
339
649
  setPerPage(value);
340
650
  setPage(1);
341
651
  }, []);
@@ -352,41 +662,41 @@ function Table({
352
662
  const handleMouseLeave = () => {
353
663
  setTooltip(null);
354
664
  };
355
- const banner = error ? /* @__PURE__ */ jsx4("div", { className: "mb-4", children: /* @__PURE__ */ jsx4(ErrorDisplay, { error }, error.message) }) : null;
665
+ const banner = error ? /* @__PURE__ */ jsx6("div", { className: "mb-4", children: /* @__PURE__ */ jsx6(ErrorDisplay, { error }, error.message) }) : null;
356
666
  if (loading) {
357
- return /* @__PURE__ */ jsxs3("div", { className: "w-full overflow-x-auto", children: [
667
+ return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
358
668
  banner,
359
- /* @__PURE__ */ jsx4("div", { className: "rounded-lg shadow-md border border-gray-200", children: /* @__PURE__ */ jsx4("div", { className: "flex items-center justify-center py-20 text-gray-500", children: "Chargement..." }) })
669
+ /* @__PURE__ */ jsx6("div", { className: "rounded-lg shadow-md border border-gray-200", children: /* @__PURE__ */ jsx6("div", { className: "flex items-center justify-center py-20 text-gray-500", children: "Chargement..." }) })
360
670
  ] });
361
671
  }
362
672
  if (!data || data.length === 0) {
363
- return /* @__PURE__ */ jsxs3("div", { className: "w-full overflow-x-auto", children: [
673
+ return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
364
674
  banner,
365
- /* @__PURE__ */ jsx4("div", { className: "rounded-lg shadow-md border border-gray-200", children: /* @__PURE__ */ jsx4("div", { className: "flex items-center justify-center py-20 text-gray-500", children: "Aucun \xE9l\xE9ment" }) })
675
+ /* @__PURE__ */ jsx6("div", { className: "rounded-lg shadow-md border border-gray-200", children: /* @__PURE__ */ jsx6("div", { className: "flex items-center justify-center py-20 text-gray-500", children: "Aucun \xE9l\xE9ment" }) })
366
676
  ] });
367
677
  }
368
- return /* @__PURE__ */ jsxs3("div", { className: "w-full overflow-x-auto", children: [
678
+ return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
369
679
  banner,
370
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-2", children: [
371
- label && /* @__PURE__ */ jsxs3("div", { className: "text-sm font-semibold text-gray-700", children: [
680
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between mb-2", children: [
681
+ label && /* @__PURE__ */ jsxs5("div", { className: "text-sm font-semibold text-gray-700", children: [
372
682
  label,
373
683
  ": ",
374
684
  data.length
375
685
  ] }),
376
- printable && /* @__PURE__ */ jsxs3(
686
+ printable && /* @__PURE__ */ jsxs5(
377
687
  "button",
378
688
  {
379
689
  onClick: () => window.print(),
380
690
  className: "flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-blue-500 rounded-lg hover:bg-blue-600 transition-colors print:hidden",
381
691
  children: [
382
- /* @__PURE__ */ jsx4(Printer, { className: "w-4 h-4" }),
692
+ /* @__PURE__ */ jsx6(Printer, { className: "w-4 h-4" }),
383
693
  "Imprimer"
384
694
  ]
385
695
  }
386
696
  )
387
697
  ] }),
388
- /* @__PURE__ */ jsx4("div", { className: "rounded-lg shadow-md border border-gray-200", children: /* @__PURE__ */ jsxs3("table", { className: "w-full table-fixed divide-y divide-gray-200 print:table-fixed", children: [
389
- /* @__PURE__ */ jsx4("thead", { className: "bg-blue-500 rounded-t-lg", children: /* @__PURE__ */ jsx4("tr", { children: columns.map((col, index) => /* @__PURE__ */ jsx4(
698
+ /* @__PURE__ */ jsx6("div", { className: "rounded-lg shadow-md border border-gray-200", children: /* @__PURE__ */ jsxs5("table", { className: "w-full table-fixed divide-y divide-gray-200 print:table-fixed", children: [
699
+ /* @__PURE__ */ jsx6("thead", { className: "bg-blue-500 rounded-t-lg", children: /* @__PURE__ */ jsx6("tr", { children: columns.map((col, index) => /* @__PURE__ */ jsx6(
390
700
  "th",
391
701
  {
392
702
  style: { width: colWidth },
@@ -398,21 +708,21 @@ function Table({
398
708
  },
399
709
  index
400
710
  )) }) }),
401
- /* @__PURE__ */ jsx4(
711
+ /* @__PURE__ */ jsx6(
402
712
  "tbody",
403
713
  {
404
714
  className: clsx(
405
715
  "bg-white divide-y divide-gray-200",
406
716
  printable && "print:hidden"
407
717
  ),
408
- children: displayData.map((row, rowIndex) => /* @__PURE__ */ jsx4(
718
+ children: displayData.map((row, rowIndex) => /* @__PURE__ */ jsx6(
409
719
  "tr",
410
720
  {
411
721
  className: "hover:bg-gray-100 transition-colors duration-200",
412
722
  children: columns.map((col, colIndex) => {
413
723
  const cellContent = col.render ? col.render(row) : String(row[col.accessor]);
414
724
  const textContent = typeof cellContent === "string" ? cellContent : "";
415
- return /* @__PURE__ */ jsx4(
725
+ return /* @__PURE__ */ jsx6(
416
726
  "td",
417
727
  {
418
728
  style: { width: colWidth },
@@ -420,7 +730,7 @@ function Table({
420
730
  "px-6 py-4 text-sm text-gray-700",
421
731
  col.excludeFromPrint && "print:hidden"
422
732
  ),
423
- children: typeof cellContent === "string" ? /* @__PURE__ */ jsx4(
733
+ children: typeof cellContent === "string" ? /* @__PURE__ */ jsx6(
424
734
  "div",
425
735
  {
426
736
  className: "truncate",
@@ -438,9 +748,9 @@ function Table({
438
748
  ))
439
749
  }
440
750
  ),
441
- printable && /* @__PURE__ */ jsx4("tbody", { className: "hidden print:table-row-group", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx4("tr", { children: columns.map((col, colIndex) => {
751
+ printable && /* @__PURE__ */ jsx6("tbody", { className: "hidden print:table-row-group", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx6("tr", { children: columns.map((col, colIndex) => {
442
752
  const cellContent = col.render ? col.render(row) : String(row[col.accessor]);
443
- return /* @__PURE__ */ jsx4(
753
+ return /* @__PURE__ */ jsx6(
444
754
  "td",
445
755
  {
446
756
  style: { width: colWidth },
@@ -454,21 +764,21 @@ function Table({
454
764
  );
455
765
  }) }, rowIndex)) })
456
766
  ] }) }),
457
- showPagination && totalPages > 1 && /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mt-4 text-sm print:hidden", children: [
458
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-gray-600", children: [
459
- /* @__PURE__ */ jsx4("span", { children: "Lignes par page :" }),
460
- /* @__PURE__ */ jsx4(
767
+ showPagination && totalPages > 1 && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between mt-4 text-sm print:hidden", children: [
768
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-gray-600", children: [
769
+ /* @__PURE__ */ jsx6("span", { children: "Lignes par page :" }),
770
+ /* @__PURE__ */ jsx6(
461
771
  "select",
462
772
  {
463
773
  value: perPage,
464
774
  onChange: (e) => handlePageSizeChange(Number(e.target.value)),
465
775
  className: "border border-gray-300 rounded px-2 py-1 text-sm bg-white focus:outline-none focus:ring-2 focus:ring-blue-400",
466
- children: pageSizeOptions.map((opt) => /* @__PURE__ */ jsx4("option", { value: opt, children: opt }, opt))
776
+ children: pageSizeOptions.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt, children: opt }, opt))
467
777
  }
468
778
  )
469
779
  ] }),
470
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
471
- /* @__PURE__ */ jsx4(
780
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1", children: [
781
+ /* @__PURE__ */ jsx6(
472
782
  "button",
473
783
  {
474
784
  onClick: () => setPage((p) => Math.max(1, p - 1)),
@@ -477,11 +787,11 @@ function Table({
477
787
  "p-1.5 rounded transition-colors",
478
788
  safePage <= 1 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"
479
789
  ),
480
- children: /* @__PURE__ */ jsx4(ChevronLeft, { className: "w-4 h-4" })
790
+ children: /* @__PURE__ */ jsx6(ChevronLeft, { className: "w-4 h-4" })
481
791
  }
482
792
  ),
483
793
  paginationRange.map(
484
- (item, i) => item === "..." ? /* @__PURE__ */ jsx4("span", { className: "px-2 text-gray-400", children: "..." }, `ellipsis-${i}`) : /* @__PURE__ */ jsx4(
794
+ (item, i) => item === "..." ? /* @__PURE__ */ jsx6("span", { className: "px-2 text-gray-400", children: "..." }, `ellipsis-${i}`) : /* @__PURE__ */ jsx6(
485
795
  "button",
486
796
  {
487
797
  onClick: () => setPage(item),
@@ -494,7 +804,7 @@ function Table({
494
804
  item
495
805
  )
496
806
  ),
497
- /* @__PURE__ */ jsx4(
807
+ /* @__PURE__ */ jsx6(
498
808
  "button",
499
809
  {
500
810
  onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
@@ -503,18 +813,18 @@ function Table({
503
813
  "p-1.5 rounded transition-colors",
504
814
  safePage >= totalPages ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"
505
815
  ),
506
- children: /* @__PURE__ */ jsx4(ChevronRight, { className: "w-4 h-4" })
816
+ children: /* @__PURE__ */ jsx6(ChevronRight, { className: "w-4 h-4" })
507
817
  }
508
818
  )
509
819
  ] }),
510
- /* @__PURE__ */ jsxs3("div", { className: "text-gray-500", children: [
820
+ /* @__PURE__ */ jsxs5("div", { className: "text-gray-500", children: [
511
821
  "Page ",
512
822
  safePage,
513
823
  " sur ",
514
824
  totalPages
515
825
  ] })
516
826
  ] }),
517
- tooltip && /* @__PURE__ */ jsx4(
827
+ tooltip && /* @__PURE__ */ jsx6(
518
828
  "div",
519
829
  {
520
830
  className: "fixed z-20 px-3 py-2 text-sm text-white bg-gray-800 rounded-lg shadow-lg pointer-events-none whitespace-nowrap",
@@ -526,7 +836,7 @@ function Table({
526
836
  children: tooltip.content
527
837
  }
528
838
  ),
529
- /* @__PURE__ */ jsx4("style", { children: `
839
+ /* @__PURE__ */ jsx6("style", { children: `
530
840
  @media print {
531
841
  body * { visibility: hidden; }
532
842
  .w-full.overflow-x-auto { visibility: visible; }
@@ -538,15 +848,15 @@ function Table({
538
848
  }
539
849
 
540
850
  // packages/ui/src/table/actions-menu.tsx
541
- import { useState as useState4, useRef, useEffect as useEffect2 } from "react";
851
+ import { useState as useState5, useRef as useRef2, useEffect as useEffect4 } from "react";
542
852
  import { Ellipsis } from "lucide-react";
543
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
853
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
544
854
  function ActionsMenu({ row, actions }) {
545
- const [isOpen, setIsOpen] = useState4(false);
546
- const [coords, setCoords] = useState4({ top: 0, left: 0 });
547
- const buttonRef = useRef(null);
548
- const menuRef = useRef(null);
549
- useEffect2(() => {
855
+ const [isOpen, setIsOpen] = useState5(false);
856
+ const [coords, setCoords] = useState5({ top: 0, left: 0 });
857
+ const buttonRef = useRef2(null);
858
+ const menuRef = useRef2(null);
859
+ useEffect4(() => {
550
860
  function handleClickOutside(event) {
551
861
  if (menuRef.current && buttonRef.current && !menuRef.current.contains(event.target) && !buttonRef.current.contains(event.target)) {
552
862
  setIsOpen(false);
@@ -574,17 +884,17 @@ function ActionsMenu({ row, actions }) {
574
884
  }
575
885
  setIsOpen(!isOpen);
576
886
  };
577
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
578
- /* @__PURE__ */ jsx5(
887
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
888
+ /* @__PURE__ */ jsx7(
579
889
  "button",
580
890
  {
581
891
  ref: buttonRef,
582
892
  onClick: toggleMenu,
583
893
  className: "p-1 hover:bg-gray-100 rounded-full",
584
- children: /* @__PURE__ */ jsx5(Ellipsis, { className: "w-5 h-5" })
894
+ children: /* @__PURE__ */ jsx7(Ellipsis, { className: "w-5 h-5" })
585
895
  }
586
896
  ),
587
- isOpen && /* @__PURE__ */ jsx5(
897
+ isOpen && /* @__PURE__ */ jsx7(
588
898
  "div",
589
899
  {
590
900
  ref: menuRef,
@@ -594,7 +904,7 @@ function ActionsMenu({ row, actions }) {
594
904
  left: coords.left,
595
905
  minWidth: 180
596
906
  },
597
- children: actions.map((action, idx) => /* @__PURE__ */ jsxs4(
907
+ children: actions.map((action, idx) => /* @__PURE__ */ jsxs6(
598
908
  "button",
599
909
  {
600
910
  className: `flex items-center gap-2 px-4 py-2 w-full text-sm hover:bg-gray-100 ${action.color || "text-gray-700"}`,
@@ -614,202 +924,6 @@ function ActionsMenu({ row, actions }) {
614
924
  ] });
615
925
  }
616
926
 
617
- // packages/ui/src/modals/modal.tsx
618
- import { X as X2 } from "lucide-react";
619
- import { useEffect as useEffect3 } from "react";
620
- import { createPortal } from "react-dom";
621
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
622
- function Modal({ title, isOpen, onClose, children }) {
623
- const requestClose = () => {
624
- onClose();
625
- };
626
- useEffect3(() => {
627
- if (isOpen) {
628
- document.body.style.overflow = "hidden";
629
- }
630
- return () => {
631
- document.body.style.overflow = "";
632
- };
633
- }, [isOpen]);
634
- useEffect3(() => {
635
- function handleEsc(e) {
636
- if (e.key === "Escape") {
637
- onClose();
638
- }
639
- }
640
- if (isOpen) {
641
- document.addEventListener("keydown", handleEsc);
642
- }
643
- return () => {
644
- document.removeEventListener("keydown", handleEsc);
645
- };
646
- }, [isOpen, onClose]);
647
- if (!isOpen) return null;
648
- const content = typeof children === "function" ? children(requestClose) : children;
649
- return createPortal(
650
- /* @__PURE__ */ jsxs5("div", { className: "fixed inset-0 z-50 flex items-center justify-center", children: [
651
- /* @__PURE__ */ jsx6(
652
- "div",
653
- {
654
- className: "absolute inset-0 bg-black/20 backdrop-blur-sm",
655
- onClick: onClose
656
- }
657
- ),
658
- /* @__PURE__ */ jsxs5("div", { className: "relative bg-white rounded-lg shadow-lg w-full max-w-2xl mx-4 p-6 flex flex-col max-h-[85vh]", children: [
659
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between mb-4 border-b-2 border-gray-400 pb-3 sticky top-0 bg-white z-10", children: [
660
- /* @__PURE__ */ jsx6("h2", { className: "text-lg font-semibold text-gray-900", children: title }),
661
- /* @__PURE__ */ jsx6(
662
- "button",
663
- {
664
- onClick: onClose,
665
- className: "text-gray-500 hover:text-gray-700 text-xl cursor-pointer",
666
- children: /* @__PURE__ */ jsx6(X2, {})
667
- }
668
- )
669
- ] }),
670
- /* @__PURE__ */ jsx6("div", { className: "overflow-y-auto flex-1 min-h-0", children: content })
671
- ] })
672
- ] }),
673
- document.body
674
- );
675
- }
676
-
677
- // packages/ui/src/feedback/confirmation.tsx
678
- import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef2, useState as useState5 } from "react";
679
- import { CircleQuestionMark } from "lucide-react";
680
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
681
- var ERROR_COUNTDOWN = 10;
682
- function Confirmation({
683
- question,
684
- messageYes,
685
- messageNo,
686
- requestClose,
687
- onAction,
688
- isProcessing,
689
- error,
690
- resetRequest
691
- }) {
692
- const [countdown, setCountdown] = useState5(0);
693
- const countdownRef = useRef2(null);
694
- const resetRequestRef = useRef2(resetRequest);
695
- useEffect4(() => {
696
- resetRequestRef.current = resetRequest;
697
- }, [resetRequest]);
698
- const clearCountdown = useCallback2(() => {
699
- if (countdownRef.current !== null) {
700
- window.clearInterval(countdownRef.current);
701
- countdownRef.current = null;
702
- }
703
- }, []);
704
- const resetConfirmationState = useCallback2(() => {
705
- clearCountdown();
706
- setCountdown(0);
707
- }, [clearCountdown]);
708
- const handleResetRequest = useCallback2(() => {
709
- resetConfirmationState();
710
- resetRequest();
711
- }, [resetConfirmationState, resetRequest]);
712
- const handleRequestClose = useCallback2(() => {
713
- resetConfirmationState();
714
- resetRequest();
715
- requestClose();
716
- }, [requestClose, resetConfirmationState, resetRequest]);
717
- const handleAction = useCallback2(async () => {
718
- try {
719
- await onAction();
720
- handleRequestClose();
721
- } catch {
722
- }
723
- }, [handleRequestClose, onAction]);
724
- useEffect4(() => {
725
- clearCountdown();
726
- if (!error) {
727
- const resetTimeoutId = window.setTimeout(() => {
728
- setCountdown(0);
729
- }, 0);
730
- return () => {
731
- window.clearTimeout(resetTimeoutId);
732
- };
733
- }
734
- const startCountdownTimeoutId = window.setTimeout(() => {
735
- setCountdown(ERROR_COUNTDOWN);
736
- countdownRef.current = window.setInterval(() => {
737
- setCountdown((previousCountdown) => {
738
- if (previousCountdown <= 1) {
739
- clearCountdown();
740
- window.setTimeout(() => {
741
- resetRequestRef.current();
742
- }, 0);
743
- return 0;
744
- }
745
- return previousCountdown - 1;
746
- });
747
- }, 1e3);
748
- }, 0);
749
- return () => {
750
- window.clearTimeout(startCountdownTimeoutId);
751
- clearCountdown();
752
- };
753
- }, [clearCountdown, error]);
754
- useEffect4(() => {
755
- return () => {
756
- clearCountdown();
757
- resetRequestRef.current();
758
- };
759
- }, [clearCountdown]);
760
- const isErrorVisible = Boolean(error) && countdown > 0;
761
- if (!question) return null;
762
- return /* @__PURE__ */ jsxs6("div", { className: "flex flex-col items-center gap-3 z-40", children: [
763
- /* @__PURE__ */ jsx7(CircleQuestionMark, { className: "text-red-500 text-5xl" }),
764
- /* @__PURE__ */ jsx7("h2", { className: "text-xl font-bold text-red-500", children: "Confirmation requise" }),
765
- /* @__PURE__ */ jsx7("p", { className: "mt-4 text-md leading-relaxed text-gray-700", children: question }),
766
- /* @__PURE__ */ jsxs6("div", { className: "mt-6 flex w-full gap-3", children: [
767
- /* @__PURE__ */ jsx7(
768
- "button",
769
- {
770
- type: "button",
771
- onClick: handleRequestClose,
772
- disabled: isProcessing,
773
- className: "w-full rounded-lg border border-gray-300 bg-white px-4 py-2 text-sm font-semibold text-gray-700 transition hover:cursor-pointer hover:bg-gray-100 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-white",
774
- children: messageNo
775
- }
776
- ),
777
- /* @__PURE__ */ jsx7(
778
- "button",
779
- {
780
- type: "button",
781
- onClick: handleAction,
782
- disabled: isProcessing,
783
- className: "w-full rounded-lg bg-red-400 px-4 py-2 text-sm font-semibold text-white transition hover:cursor-pointer hover:bg-red-500 active:scale-95 disabled:opacity-50 disabled:hover:bg-red-500",
784
- children: isProcessing ? "operation en cours" : messageYes
785
- }
786
- )
787
- ] }),
788
- error && isErrorVisible && /* @__PURE__ */ jsxs6("div", { className: "mt-4 w-full rounded-lg border border-red-200 bg-red-50 p-4 text-left text-sm text-red-700", children: [
789
- /* @__PURE__ */ jsxs6("div", { className: "flex items-start justify-between gap-3", children: [
790
- /* @__PURE__ */ jsxs6("div", { children: [
791
- /* @__PURE__ */ jsx7("p", { className: "font-semibold", children: "Erreur" }),
792
- /* @__PURE__ */ jsx7("p", { className: "mt-1 text-sm", children: error.message })
793
- ] }),
794
- /* @__PURE__ */ jsx7(
795
- "button",
796
- {
797
- type: "button",
798
- onClick: handleResetRequest,
799
- className: "rounded-full bg-red-100 px-2 py-1 text-xs font-semibold text-red-700 hover:bg-red-200",
800
- children: "Fermer"
801
- }
802
- )
803
- ] }),
804
- /* @__PURE__ */ jsxs6("p", { className: "mt-3 text-xs text-red-600", children: [
805
- "Ce message disparait dans ",
806
- countdown,
807
- "s..."
808
- ] })
809
- ] })
810
- ] });
811
- }
812
-
813
927
  // packages/ui/src/headers/header-content.tsx
814
928
  import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
815
929
  function HeaderContent({
@@ -1243,45 +1357,6 @@ function findAllowedRoles(page, pagePermissions) {
1243
1357
  }
1244
1358
  return void 0;
1245
1359
  }
1246
-
1247
- // packages/utils/src/fetcher.ts
1248
- var ApiError = class extends Error {
1249
- status;
1250
- code;
1251
- data;
1252
- constructor(params) {
1253
- super(params.message);
1254
- this.status = params.status;
1255
- this.code = params.code;
1256
- this.data = params.data;
1257
- }
1258
- };
1259
- async function fetcher(url, options = {}) {
1260
- const { headers, skipJsonParsing = false, ...rest } = options;
1261
- const response = await fetch(url, {
1262
- credentials: "include",
1263
- headers: {
1264
- "Content-Type": "application/json",
1265
- ...headers
1266
- },
1267
- ...rest
1268
- });
1269
- let responseData = null;
1270
- const contentType = response.headers.get("content-type");
1271
- if (!skipJsonParsing && contentType?.includes("application/json")) {
1272
- responseData = await response.json();
1273
- }
1274
- if (!response.ok) {
1275
- const backendError = responseData;
1276
- throw new ApiError({
1277
- message: backendError?.message || "Une erreur est survenue",
1278
- status: backendError?.status || response.status,
1279
- code: backendError?.code,
1280
- data: responseData
1281
- });
1282
- }
1283
- return responseData;
1284
- }
1285
1360
  export {
1286
1361
  AccessDenied,
1287
1362
  ActionsMenu,