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