@ramses1er/cbk-toolkit 2.0.0 → 2.1.1

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,280 @@
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 findActiveTitleSource(mainItems, footer, currentPath) {
253
+ let best;
254
+ const consider = (item) => {
255
+ if (!isActive(item.href, currentPath)) return;
256
+ if (!best || item.href.length > best.href.length) {
257
+ best = item;
258
+ }
259
+ };
260
+ const visit = (items) => {
261
+ for (const item of items) {
262
+ consider(item);
263
+ if (item.subItems && item.subItems.length > 0) visit(item.subItems);
264
+ }
265
+ };
266
+ visit(mainItems);
267
+ footer?.forEach((item) => {
268
+ if (!("confirm" in item)) consider(item);
269
+ });
270
+ return best;
271
+ }
272
+ function renderItemIcon(icon) {
273
+ if (!icon) return null;
274
+ if (typeof icon === "string") return /* @__PURE__ */ jsx3("span", { className: "text-lg", children: icon });
275
+ if (isValidElement(icon)) return icon;
276
+ return createElement(icon, { size: 20 });
277
+ }
15
278
  function SideNav({
16
279
  children,
17
280
  title,
@@ -22,22 +285,23 @@ function SideNav({
22
285
  toggleIcon
23
286
  }) {
24
287
  const activeMain = mainItems.find((item) => isActive(item.href, currentPath));
25
- const headerTitle = title ?? activeMain?.title ?? activeMain?.label;
26
- const [expandedSection, setExpandedSection] = useState(
288
+ const activeTitleSource = findActiveTitleSource(mainItems, footer, currentPath);
289
+ const headerTitle = title ?? activeTitleSource?.title ?? activeTitleSource?.label ?? activeMain?.label;
290
+ const [expandedSection, setExpandedSection] = useState2(
27
291
  activeMain?.id ?? null
28
292
  );
29
293
  const toggleSection = (id) => {
30
294
  setExpandedSection((prev) => prev === id ? null : id);
31
295
  };
32
- const [isMobileOpen, setIsMobileOpen] = useState(false);
33
- useEffect(() => {
296
+ const [isMobileOpen, setIsMobileOpen] = useState2(false);
297
+ useEffect3(() => {
34
298
  const handleEscape = (e) => {
35
299
  if (e.key === "Escape") setIsMobileOpen(false);
36
300
  };
37
301
  document.addEventListener("keydown", handleEscape);
38
302
  return () => document.removeEventListener("keydown", handleEscape);
39
303
  }, []);
40
- useEffect(() => {
304
+ useEffect3(() => {
41
305
  if (isMobileOpen) {
42
306
  document.body.style.overflow = "hidden";
43
307
  } else {
@@ -48,14 +312,45 @@ function SideNav({
48
312
  };
49
313
  }, [isMobileOpen]);
50
314
  const closeMobileNav = () => setIsMobileOpen(false);
51
- return /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen", children: [
52
- /* @__PURE__ */ jsxs(
315
+ const [confirmingItem, setConfirmingItem] = useState2(null);
316
+ const [pending, setPending] = useState2(false);
317
+ const [error, setError] = useState2(null);
318
+ const openConfirm = (item) => {
319
+ setError(null);
320
+ setConfirmingItem(item);
321
+ closeMobileNav();
322
+ };
323
+ const closeConfirm = () => {
324
+ if (pending) return;
325
+ setConfirmingItem(null);
326
+ setError(null);
327
+ };
328
+ const handleConfirmAction = async () => {
329
+ if (!confirmingItem || !("confirm" in confirmingItem)) return;
330
+ setPending(true);
331
+ setError(null);
332
+ try {
333
+ await confirmingItem.confirm.onAction();
334
+ } catch (e) {
335
+ setError(
336
+ e instanceof ApiError ? e : new ApiError({
337
+ message: e instanceof Error ? e.message : "Une erreur est survenue",
338
+ status: 500
339
+ })
340
+ );
341
+ throw e;
342
+ } finally {
343
+ setPending(false);
344
+ }
345
+ };
346
+ return /* @__PURE__ */ jsxs3("div", { className: "flex min-h-screen", children: [
347
+ /* @__PURE__ */ jsxs3(
53
348
  "aside",
54
349
  {
55
350
  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
351
  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(
352
+ 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: [
353
+ brand.logo && (typeof brand.logo === "string" ? /* @__PURE__ */ jsx3(
59
354
  "img",
60
355
  {
61
356
  src: brand.logo,
@@ -63,22 +358,22 @@ function SideNav({
63
358
  className: "w-12.5 h-12.5"
64
359
  }
65
360
  ) : brand.logo),
66
- brand.name && /* @__PURE__ */ jsx("p", { className: "text-gray-500 text-base font-semibold", children: brand.name })
361
+ brand.name && /* @__PURE__ */ jsx3("p", { className: "text-gray-500 text-base font-semibold", children: brand.name })
67
362
  ] }) }),
68
- /* @__PURE__ */ jsx("nav", { className: "flex-1 p-4 overflow-y-auto", children: /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: mainItems.map((item) => {
363
+ /* @__PURE__ */ jsx3("nav", { className: "flex-1 p-4 overflow-y-auto", children: /* @__PURE__ */ jsx3("ul", { className: "space-y-1", children: mainItems.map((item) => {
69
364
  const active = isActive(item.href, currentPath);
70
365
  const isExpanded = expandedSection === item.id;
71
366
  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(
367
+ return /* @__PURE__ */ jsxs3("li", { children: [
368
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
369
+ /* @__PURE__ */ jsxs3(
75
370
  "a",
76
371
  {
77
372
  href: item.href,
78
373
  onClick: closeMobileNav,
79
374
  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
375
  children: [
81
- item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx("span", { className: "text-lg", children: item.icon }) : isValidElement(item.icon) ? item.icon : createElement(
376
+ item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx3("span", { className: "text-lg", children: item.icon }) : isValidElement(item.icon) ? item.icon : createElement(
82
377
  item.icon,
83
378
  { size: 20 }
84
379
  )),
@@ -86,27 +381,27 @@ function SideNav({
86
381
  ]
87
382
  }
88
383
  ),
89
- active && hasSubItems && /* @__PURE__ */ jsx(
384
+ active && hasSubItems && /* @__PURE__ */ jsx3(
90
385
  "button",
91
386
  {
92
387
  onClick: () => toggleSection(item.id),
93
388
  className: `p-1.5 rounded-lg transition-colors ${active ? "text-black hover:bg-gray-100" : "text-gray-500 hover:bg-gray-100"}`,
94
389
  title: isExpanded ? "R\xE9duire" : "D\xE9velopper",
95
- children: toggleIcon ? /* @__PURE__ */ jsx(
390
+ children: toggleIcon ? /* @__PURE__ */ jsx3(
96
391
  "span",
97
392
  {
98
393
  className: `inline-block transition-transform duration-200 ${isExpanded ? "rotate-0" : "-rotate-90"}`,
99
394
  children: toggleIcon
100
395
  }
101
- ) : /* @__PURE__ */ jsx("span", { className: "text-xs", children: isExpanded ? "\u25BC" : "\u25B6" })
396
+ ) : /* @__PURE__ */ jsx3("span", { className: "text-xs", children: isExpanded ? "\u25BC" : "\u25B6" })
102
397
  }
103
398
  )
104
399
  ] }),
105
- active && hasSubItems && /* @__PURE__ */ jsx(
400
+ active && hasSubItems && /* @__PURE__ */ jsx3(
106
401
  "div",
107
402
  {
108
403
  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(
404
+ 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
405
  "a",
111
406
  {
112
407
  href: sub.href,
@@ -119,25 +414,61 @@ function SideNav({
119
414
  )
120
415
  ] }, item.id);
121
416
  }) }) }),
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
- )) })
417
+ footer && footer.length > 0 && /* @__PURE__ */ jsx3("div", { className: "p-4 border-t border-gray-100 space-y-1", children: footer.map(
418
+ (item) => "confirm" in item ? /* @__PURE__ */ jsxs3(
419
+ "button",
420
+ {
421
+ type: "button",
422
+ onClick: () => openConfirm(item),
423
+ 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",
424
+ children: [
425
+ renderItemIcon(item.icon),
426
+ item.label
427
+ ]
428
+ },
429
+ item.label
430
+ ) : /* @__PURE__ */ jsxs3(
431
+ "a",
432
+ {
433
+ href: item.href,
434
+ onClick: closeMobileNav,
435
+ 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",
436
+ children: [
437
+ renderItemIcon(item.icon),
438
+ item.label
439
+ ]
440
+ },
441
+ item.label
442
+ )
443
+ ) })
137
444
  ]
138
445
  }
139
446
  ),
140
- isMobileOpen && /* @__PURE__ */ jsx(
447
+ confirmingItem && "confirm" in confirmingItem && /* @__PURE__ */ jsx3(
448
+ Modal,
449
+ {
450
+ title: confirmingItem.confirm.title ?? confirmingItem.label,
451
+ isOpen: true,
452
+ onClose: closeConfirm,
453
+ children: (requestClose) => /* @__PURE__ */ jsx3(
454
+ Confirmation,
455
+ {
456
+ question: confirmingItem.confirm.question,
457
+ messageYes: confirmingItem.confirm.messageYes,
458
+ messageNo: confirmingItem.confirm.messageNo,
459
+ requestClose: () => {
460
+ setConfirmingItem(null);
461
+ setError(null);
462
+ },
463
+ onAction: handleConfirmAction,
464
+ isProcessing: pending,
465
+ error,
466
+ resetRequest: () => setError(null)
467
+ }
468
+ )
469
+ }
470
+ ),
471
+ isMobileOpen && /* @__PURE__ */ jsx3(
141
472
  "div",
142
473
  {
143
474
  className: "fixed inset-0 z-40 bg-black/50 md:hidden",
@@ -145,17 +476,17 @@ function SideNav({
145
476
  "aria-hidden": "true"
146
477
  }
147
478
  ),
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(
479
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 ml-64 max-md:ml-0 min-w-0", children: [
480
+ 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: [
481
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
482
+ /* @__PURE__ */ jsx3(
152
483
  "button",
153
484
  {
154
485
  className: "md:hidden p-2 -ml-2 rounded-lg hover:bg-gray-100 transition-colors",
155
486
  onClick: () => setIsMobileOpen(!isMobileOpen),
156
487
  "aria-label": isMobileOpen ? "Fermer le menu" : "Menu",
157
488
  "aria-expanded": isMobileOpen,
158
- children: isMobileOpen ? /* @__PURE__ */ jsx(
489
+ children: isMobileOpen ? /* @__PURE__ */ jsx3(
159
490
  "svg",
160
491
  {
161
492
  className: "w-5 h-5",
@@ -163,7 +494,7 @@ function SideNav({
163
494
  viewBox: "0 0 24 24",
164
495
  stroke: "currentColor",
165
496
  strokeWidth: 2,
166
- children: /* @__PURE__ */ jsx(
497
+ children: /* @__PURE__ */ jsx3(
167
498
  "path",
168
499
  {
169
500
  strokeLinecap: "round",
@@ -172,7 +503,7 @@ function SideNav({
172
503
  }
173
504
  )
174
505
  }
175
- ) : /* @__PURE__ */ jsx(
506
+ ) : /* @__PURE__ */ jsx3(
176
507
  "svg",
177
508
  {
178
509
  className: "w-5 h-5",
@@ -180,7 +511,7 @@ function SideNav({
180
511
  viewBox: "0 0 24 24",
181
512
  stroke: "currentColor",
182
513
  strokeWidth: 2,
183
- children: /* @__PURE__ */ jsx(
514
+ children: /* @__PURE__ */ jsx3(
184
515
  "path",
185
516
  {
186
517
  strokeLinecap: "round",
@@ -192,18 +523,18 @@ function SideNav({
192
523
  )
193
524
  }
194
525
  ),
195
- /* @__PURE__ */ jsx("h1", { className: "text-base font-semibold text-gray-500", children: headerTitle })
526
+ /* @__PURE__ */ jsx3("h1", { className: "text-base font-semibold text-gray-500", children: headerTitle })
196
527
  ] }),
197
- brand?.tagline && /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-500 max-md:hidden", children: brand.tagline })
528
+ brand?.tagline && /* @__PURE__ */ jsx3("span", { className: "text-sm text-gray-500 max-md:hidden", children: brand.tagline })
198
529
  ] }),
199
- !headerTitle && /* @__PURE__ */ jsx(
530
+ !headerTitle && /* @__PURE__ */ jsx3(
200
531
  "button",
201
532
  {
202
533
  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
534
  onClick: () => setIsMobileOpen(!isMobileOpen),
204
535
  "aria-label": isMobileOpen ? "Fermer le menu" : "Menu",
205
536
  "aria-expanded": isMobileOpen,
206
- children: isMobileOpen ? /* @__PURE__ */ jsx(
537
+ children: isMobileOpen ? /* @__PURE__ */ jsx3(
207
538
  "svg",
208
539
  {
209
540
  className: "w-5 h-5",
@@ -211,7 +542,7 @@ function SideNav({
211
542
  viewBox: "0 0 24 24",
212
543
  stroke: "currentColor",
213
544
  strokeWidth: 2,
214
- children: /* @__PURE__ */ jsx(
545
+ children: /* @__PURE__ */ jsx3(
215
546
  "path",
216
547
  {
217
548
  strokeLinecap: "round",
@@ -220,7 +551,7 @@ function SideNav({
220
551
  }
221
552
  )
222
553
  }
223
- ) : /* @__PURE__ */ jsx(
554
+ ) : /* @__PURE__ */ jsx3(
224
555
  "svg",
225
556
  {
226
557
  className: "w-5 h-5",
@@ -228,7 +559,7 @@ function SideNav({
228
559
  viewBox: "0 0 24 24",
229
560
  stroke: "currentColor",
230
561
  strokeWidth: 2,
231
- children: /* @__PURE__ */ jsx(
562
+ children: /* @__PURE__ */ jsx3(
232
563
  "path",
233
564
  {
234
565
  strokeLinecap: "round",
@@ -240,7 +571,7 @@ function SideNav({
240
571
  )
241
572
  }
242
573
  ),
243
- /* @__PURE__ */ jsx(
574
+ /* @__PURE__ */ jsx3(
244
575
  "main",
245
576
  {
246
577
  className: (headerTitle ? "p-6 pt-20" : "p-6") + " overflow-x-auto",
@@ -252,46 +583,46 @@ function SideNav({
252
583
  }
253
584
 
254
585
  // packages/ui/src/config/create-side-nav.tsx
255
- import { jsx as jsx2 } from "react/jsx-runtime";
586
+ import { jsx as jsx4 } from "react/jsx-runtime";
256
587
  function createSideNav(config) {
257
588
  return function ConfiguredSideNav({
258
589
  children,
259
590
  currentPath
260
591
  }) {
261
- return /* @__PURE__ */ jsx2(SideNav, { ...config, currentPath, children });
592
+ return /* @__PURE__ */ jsx4(SideNav, { ...config, currentPath, children });
262
593
  };
263
594
  }
264
595
 
265
596
  // packages/ui/src/table/table.tsx
266
597
  import { Printer, ChevronLeft, ChevronRight } from "lucide-react";
267
598
  import clsx from "clsx";
268
- import { useState as useState3, useMemo, useCallback } from "react";
599
+ import { useState as useState4, useMemo, useCallback as useCallback2 } from "react";
269
600
 
270
601
  // 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";
602
+ import { X as X2 } from "lucide-react";
603
+ import { useState as useState3 } from "react";
604
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
274
605
  function ErrorDisplay({ error }) {
275
- const [dismissed, setDismissed] = useState2(false);
606
+ const [dismissed, setDismissed] = useState3(false);
276
607
  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 })
608
+ 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: [
609
+ /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-1", children: [
610
+ /* @__PURE__ */ jsx5("p", { className: "text-red-700 font-medium", children: "Erreur" }),
611
+ /* @__PURE__ */ jsx5("p", { className: "text-red-600 text-sm", children: error.message })
281
612
  ] }),
282
- /* @__PURE__ */ jsx3(
613
+ /* @__PURE__ */ jsx5(
283
614
  "button",
284
615
  {
285
616
  onClick: () => setDismissed(true),
286
617
  className: "text-red-400 hover:text-red-600 shrink-0 cursor-pointer",
287
- children: /* @__PURE__ */ jsx3(X, { className: "w-5 h-5" })
618
+ children: /* @__PURE__ */ jsx5(X2, { className: "w-5 h-5" })
288
619
  }
289
620
  )
290
621
  ] }) }) });
291
622
  }
292
623
 
293
624
  // packages/ui/src/table/table.tsx
294
- import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
625
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
295
626
  var VISIBLE_PAGES = 5;
296
627
  function Table({
297
628
  data,
@@ -304,9 +635,9 @@ function Table({
304
635
  pageSizeOptions = [5, 10, 20],
305
636
  showPagination = true
306
637
  }) {
307
- const [tooltip, setTooltip] = useState3(null);
308
- const [page, setPage] = useState3(1);
309
- const [perPage, setPerPage] = useState3(pageSize);
638
+ const [tooltip, setTooltip] = useState4(null);
639
+ const [page, setPage] = useState4(1);
640
+ const [perPage, setPerPage] = useState4(pageSize);
310
641
  const totalPages = useMemo(
311
642
  () => Math.max(1, Math.ceil(data.length / perPage)),
312
643
  [data.length, perPage]
@@ -335,7 +666,7 @@ function Table({
335
666
  }
336
667
  return range;
337
668
  }, [safePage, totalPages]);
338
- const handlePageSizeChange = useCallback((value) => {
669
+ const handlePageSizeChange = useCallback2((value) => {
339
670
  setPerPage(value);
340
671
  setPage(1);
341
672
  }, []);
@@ -352,41 +683,41 @@ function Table({
352
683
  const handleMouseLeave = () => {
353
684
  setTooltip(null);
354
685
  };
355
- const banner = error ? /* @__PURE__ */ jsx4("div", { className: "mb-4", children: /* @__PURE__ */ jsx4(ErrorDisplay, { error }, error.message) }) : null;
686
+ const banner = error ? /* @__PURE__ */ jsx6("div", { className: "mb-4", children: /* @__PURE__ */ jsx6(ErrorDisplay, { error }, error.message) }) : null;
356
687
  if (loading) {
357
- return /* @__PURE__ */ jsxs3("div", { className: "w-full overflow-x-auto", children: [
688
+ return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
358
689
  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..." }) })
690
+ /* @__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
691
  ] });
361
692
  }
362
693
  if (!data || data.length === 0) {
363
- return /* @__PURE__ */ jsxs3("div", { className: "w-full overflow-x-auto", children: [
694
+ return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
364
695
  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" }) })
696
+ /* @__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
697
  ] });
367
698
  }
368
- return /* @__PURE__ */ jsxs3("div", { className: "w-full overflow-x-auto", children: [
699
+ return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
369
700
  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: [
701
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between mb-2", children: [
702
+ label && /* @__PURE__ */ jsxs5("div", { className: "text-sm font-semibold text-gray-700", children: [
372
703
  label,
373
704
  ": ",
374
705
  data.length
375
706
  ] }),
376
- printable && /* @__PURE__ */ jsxs3(
707
+ printable && /* @__PURE__ */ jsxs5(
377
708
  "button",
378
709
  {
379
710
  onClick: () => window.print(),
380
711
  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
712
  children: [
382
- /* @__PURE__ */ jsx4(Printer, { className: "w-4 h-4" }),
713
+ /* @__PURE__ */ jsx6(Printer, { className: "w-4 h-4" }),
383
714
  "Imprimer"
384
715
  ]
385
716
  }
386
717
  )
387
718
  ] }),
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(
719
+ /* @__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: [
720
+ /* @__PURE__ */ jsx6("thead", { className: "bg-blue-500 rounded-t-lg", children: /* @__PURE__ */ jsx6("tr", { children: columns.map((col, index) => /* @__PURE__ */ jsx6(
390
721
  "th",
391
722
  {
392
723
  style: { width: colWidth },
@@ -398,21 +729,21 @@ function Table({
398
729
  },
399
730
  index
400
731
  )) }) }),
401
- /* @__PURE__ */ jsx4(
732
+ /* @__PURE__ */ jsx6(
402
733
  "tbody",
403
734
  {
404
735
  className: clsx(
405
736
  "bg-white divide-y divide-gray-200",
406
737
  printable && "print:hidden"
407
738
  ),
408
- children: displayData.map((row, rowIndex) => /* @__PURE__ */ jsx4(
739
+ children: displayData.map((row, rowIndex) => /* @__PURE__ */ jsx6(
409
740
  "tr",
410
741
  {
411
742
  className: "hover:bg-gray-100 transition-colors duration-200",
412
743
  children: columns.map((col, colIndex) => {
413
744
  const cellContent = col.render ? col.render(row) : String(row[col.accessor]);
414
745
  const textContent = typeof cellContent === "string" ? cellContent : "";
415
- return /* @__PURE__ */ jsx4(
746
+ return /* @__PURE__ */ jsx6(
416
747
  "td",
417
748
  {
418
749
  style: { width: colWidth },
@@ -420,7 +751,7 @@ function Table({
420
751
  "px-6 py-4 text-sm text-gray-700",
421
752
  col.excludeFromPrint && "print:hidden"
422
753
  ),
423
- children: typeof cellContent === "string" ? /* @__PURE__ */ jsx4(
754
+ children: typeof cellContent === "string" ? /* @__PURE__ */ jsx6(
424
755
  "div",
425
756
  {
426
757
  className: "truncate",
@@ -438,9 +769,9 @@ function Table({
438
769
  ))
439
770
  }
440
771
  ),
441
- printable && /* @__PURE__ */ jsx4("tbody", { className: "hidden print:table-row-group", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx4("tr", { children: columns.map((col, colIndex) => {
772
+ printable && /* @__PURE__ */ jsx6("tbody", { className: "hidden print:table-row-group", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx6("tr", { children: columns.map((col, colIndex) => {
442
773
  const cellContent = col.render ? col.render(row) : String(row[col.accessor]);
443
- return /* @__PURE__ */ jsx4(
774
+ return /* @__PURE__ */ jsx6(
444
775
  "td",
445
776
  {
446
777
  style: { width: colWidth },
@@ -454,21 +785,21 @@ function Table({
454
785
  );
455
786
  }) }, rowIndex)) })
456
787
  ] }) }),
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(
788
+ showPagination && totalPages > 1 && /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between mt-4 text-sm print:hidden", children: [
789
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-gray-600", children: [
790
+ /* @__PURE__ */ jsx6("span", { children: "Lignes par page :" }),
791
+ /* @__PURE__ */ jsx6(
461
792
  "select",
462
793
  {
463
794
  value: perPage,
464
795
  onChange: (e) => handlePageSizeChange(Number(e.target.value)),
465
796
  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))
797
+ children: pageSizeOptions.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt, children: opt }, opt))
467
798
  }
468
799
  )
469
800
  ] }),
470
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
471
- /* @__PURE__ */ jsx4(
801
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1", children: [
802
+ /* @__PURE__ */ jsx6(
472
803
  "button",
473
804
  {
474
805
  onClick: () => setPage((p) => Math.max(1, p - 1)),
@@ -477,11 +808,11 @@ function Table({
477
808
  "p-1.5 rounded transition-colors",
478
809
  safePage <= 1 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"
479
810
  ),
480
- children: /* @__PURE__ */ jsx4(ChevronLeft, { className: "w-4 h-4" })
811
+ children: /* @__PURE__ */ jsx6(ChevronLeft, { className: "w-4 h-4" })
481
812
  }
482
813
  ),
483
814
  paginationRange.map(
484
- (item, i) => item === "..." ? /* @__PURE__ */ jsx4("span", { className: "px-2 text-gray-400", children: "..." }, `ellipsis-${i}`) : /* @__PURE__ */ jsx4(
815
+ (item, i) => item === "..." ? /* @__PURE__ */ jsx6("span", { className: "px-2 text-gray-400", children: "..." }, `ellipsis-${i}`) : /* @__PURE__ */ jsx6(
485
816
  "button",
486
817
  {
487
818
  onClick: () => setPage(item),
@@ -494,7 +825,7 @@ function Table({
494
825
  item
495
826
  )
496
827
  ),
497
- /* @__PURE__ */ jsx4(
828
+ /* @__PURE__ */ jsx6(
498
829
  "button",
499
830
  {
500
831
  onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
@@ -503,18 +834,18 @@ function Table({
503
834
  "p-1.5 rounded transition-colors",
504
835
  safePage >= totalPages ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"
505
836
  ),
506
- children: /* @__PURE__ */ jsx4(ChevronRight, { className: "w-4 h-4" })
837
+ children: /* @__PURE__ */ jsx6(ChevronRight, { className: "w-4 h-4" })
507
838
  }
508
839
  )
509
840
  ] }),
510
- /* @__PURE__ */ jsxs3("div", { className: "text-gray-500", children: [
841
+ /* @__PURE__ */ jsxs5("div", { className: "text-gray-500", children: [
511
842
  "Page ",
512
843
  safePage,
513
844
  " sur ",
514
845
  totalPages
515
846
  ] })
516
847
  ] }),
517
- tooltip && /* @__PURE__ */ jsx4(
848
+ tooltip && /* @__PURE__ */ jsx6(
518
849
  "div",
519
850
  {
520
851
  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 +857,7 @@ function Table({
526
857
  children: tooltip.content
527
858
  }
528
859
  ),
529
- /* @__PURE__ */ jsx4("style", { children: `
860
+ /* @__PURE__ */ jsx6("style", { children: `
530
861
  @media print {
531
862
  body * { visibility: hidden; }
532
863
  .w-full.overflow-x-auto { visibility: visible; }
@@ -538,15 +869,15 @@ function Table({
538
869
  }
539
870
 
540
871
  // packages/ui/src/table/actions-menu.tsx
541
- import { useState as useState4, useRef, useEffect as useEffect2 } from "react";
872
+ import { useState as useState5, useRef as useRef2, useEffect as useEffect4 } from "react";
542
873
  import { Ellipsis } from "lucide-react";
543
- import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
874
+ import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
544
875
  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(() => {
876
+ const [isOpen, setIsOpen] = useState5(false);
877
+ const [coords, setCoords] = useState5({ top: 0, left: 0 });
878
+ const buttonRef = useRef2(null);
879
+ const menuRef = useRef2(null);
880
+ useEffect4(() => {
550
881
  function handleClickOutside(event) {
551
882
  if (menuRef.current && buttonRef.current && !menuRef.current.contains(event.target) && !buttonRef.current.contains(event.target)) {
552
883
  setIsOpen(false);
@@ -574,17 +905,17 @@ function ActionsMenu({ row, actions }) {
574
905
  }
575
906
  setIsOpen(!isOpen);
576
907
  };
577
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
578
- /* @__PURE__ */ jsx5(
908
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
909
+ /* @__PURE__ */ jsx7(
579
910
  "button",
580
911
  {
581
912
  ref: buttonRef,
582
913
  onClick: toggleMenu,
583
914
  className: "p-1 hover:bg-gray-100 rounded-full",
584
- children: /* @__PURE__ */ jsx5(Ellipsis, { className: "w-5 h-5" })
915
+ children: /* @__PURE__ */ jsx7(Ellipsis, { className: "w-5 h-5" })
585
916
  }
586
917
  ),
587
- isOpen && /* @__PURE__ */ jsx5(
918
+ isOpen && /* @__PURE__ */ jsx7(
588
919
  "div",
589
920
  {
590
921
  ref: menuRef,
@@ -594,7 +925,7 @@ function ActionsMenu({ row, actions }) {
594
925
  left: coords.left,
595
926
  minWidth: 180
596
927
  },
597
- children: actions.map((action, idx) => /* @__PURE__ */ jsxs4(
928
+ children: actions.map((action, idx) => /* @__PURE__ */ jsxs6(
598
929
  "button",
599
930
  {
600
931
  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 +945,6 @@ function ActionsMenu({ row, actions }) {
614
945
  ] });
615
946
  }
616
947
 
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
948
  // packages/ui/src/headers/header-content.tsx
814
949
  import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
815
950
  function HeaderContent({
@@ -1243,45 +1378,6 @@ function findAllowedRoles(page, pagePermissions) {
1243
1378
  }
1244
1379
  return void 0;
1245
1380
  }
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
1381
  export {
1286
1382
  AccessDenied,
1287
1383
  ActionsMenu,