@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.d.ts +17 -6
- package/dist/client.js +447 -351
- package/dist/client.js.map +1 -1
- package/package.json +1 -1
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
|
|
26
|
-
const
|
|
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] =
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
|
|
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__ */
|
|
58
|
-
brand.logo && (typeof brand.logo === "string" ? /* @__PURE__ */
|
|
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__ */
|
|
361
|
+
brand.name && /* @__PURE__ */ jsx3("p", { className: "text-gray-500 text-base font-semibold", children: brand.name })
|
|
67
362
|
] }) }),
|
|
68
|
-
/* @__PURE__ */
|
|
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__ */
|
|
73
|
-
/* @__PURE__ */
|
|
74
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
396
|
+
) : /* @__PURE__ */ jsx3("span", { className: "text-xs", children: isExpanded ? "\u25BC" : "\u25B6" })
|
|
102
397
|
}
|
|
103
398
|
)
|
|
104
399
|
] }),
|
|
105
|
-
active && hasSubItems && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
123
|
-
"
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
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__ */
|
|
149
|
-
headerTitle && /* @__PURE__ */
|
|
150
|
-
/* @__PURE__ */
|
|
151
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
526
|
+
/* @__PURE__ */ jsx3("h1", { className: "text-base font-semibold text-gray-500", children: headerTitle })
|
|
196
527
|
] }),
|
|
197
|
-
brand?.tagline && /* @__PURE__ */
|
|
528
|
+
brand?.tagline && /* @__PURE__ */ jsx3("span", { className: "text-sm text-gray-500 max-md:hidden", children: brand.tagline })
|
|
198
529
|
] }),
|
|
199
|
-
!headerTitle && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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
|
|
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
|
|
273
|
-
import { jsx as
|
|
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] =
|
|
606
|
+
const [dismissed, setDismissed] = useState3(false);
|
|
276
607
|
if (!error || dismissed) return null;
|
|
277
|
-
return /* @__PURE__ */
|
|
278
|
-
/* @__PURE__ */
|
|
279
|
-
/* @__PURE__ */
|
|
280
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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] =
|
|
308
|
-
const [page, setPage] =
|
|
309
|
-
const [perPage, setPerPage] =
|
|
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 =
|
|
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__ */
|
|
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__ */
|
|
688
|
+
return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
|
|
358
689
|
banner,
|
|
359
|
-
/* @__PURE__ */
|
|
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__ */
|
|
694
|
+
return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
|
|
364
695
|
banner,
|
|
365
|
-
/* @__PURE__ */
|
|
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__ */
|
|
699
|
+
return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
|
|
369
700
|
banner,
|
|
370
|
-
/* @__PURE__ */
|
|
371
|
-
label && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
713
|
+
/* @__PURE__ */ jsx6(Printer, { className: "w-4 h-4" }),
|
|
383
714
|
"Imprimer"
|
|
384
715
|
]
|
|
385
716
|
}
|
|
386
717
|
)
|
|
387
718
|
] }),
|
|
388
|
-
/* @__PURE__ */
|
|
389
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
458
|
-
/* @__PURE__ */
|
|
459
|
-
/* @__PURE__ */
|
|
460
|
-
/* @__PURE__ */
|
|
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__ */
|
|
797
|
+
children: pageSizeOptions.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt, children: opt }, opt))
|
|
467
798
|
}
|
|
468
799
|
)
|
|
469
800
|
] }),
|
|
470
|
-
/* @__PURE__ */
|
|
471
|
-
/* @__PURE__ */
|
|
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__ */
|
|
811
|
+
children: /* @__PURE__ */ jsx6(ChevronLeft, { className: "w-4 h-4" })
|
|
481
812
|
}
|
|
482
813
|
),
|
|
483
814
|
paginationRange.map(
|
|
484
|
-
(item, i) => item === "..." ? /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
837
|
+
children: /* @__PURE__ */ jsx6(ChevronRight, { className: "w-4 h-4" })
|
|
507
838
|
}
|
|
508
839
|
)
|
|
509
840
|
] }),
|
|
510
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
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
|
|
874
|
+
import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
544
875
|
function ActionsMenu({ row, actions }) {
|
|
545
|
-
const [isOpen, setIsOpen] =
|
|
546
|
-
const [coords, setCoords] =
|
|
547
|
-
const buttonRef =
|
|
548
|
-
const menuRef =
|
|
549
|
-
|
|
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__ */
|
|
578
|
-
/* @__PURE__ */
|
|
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__ */
|
|
915
|
+
children: /* @__PURE__ */ jsx7(Ellipsis, { className: "w-5 h-5" })
|
|
585
916
|
}
|
|
586
917
|
),
|
|
587
|
-
isOpen && /* @__PURE__ */
|
|
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__ */
|
|
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,
|