@ramses1er/cbk-toolkit 2.1.1 → 2.3.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/packages/lib/src/auth-guard.d.ts +10 -0
- package/dist/packages/lib/src/auth-guard.d.ts.map +1 -0
- package/dist/packages/lib/src/auth-guard.js +60 -0
- package/dist/packages/lib/src/auth-guard.js.map +1 -0
- package/dist/packages/lib/src/handle-server-error.d.ts +9 -0
- package/dist/packages/lib/src/handle-server-error.d.ts.map +1 -0
- package/dist/packages/lib/src/handle-server-error.js +43 -0
- package/dist/packages/lib/src/handle-server-error.js.map +1 -0
- package/dist/packages/lib/src/http-error.d.ts +6 -0
- package/dist/packages/lib/src/http-error.d.ts.map +1 -0
- package/dist/packages/lib/src/http-error.js +12 -0
- package/dist/packages/lib/src/http-error.js.map +1 -0
- package/dist/packages/lib/src/index.d.ts +5 -0
- package/dist/packages/lib/src/index.d.ts.map +1 -0
- package/dist/packages/lib/src/index.js +9 -0
- package/dist/packages/lib/src/index.js.map +1 -0
- package/dist/packages/lib/src/with-auth.d.ts +8 -0
- package/dist/packages/lib/src/with-auth.d.ts.map +1 -0
- package/dist/packages/lib/src/with-auth.js +32 -0
- package/dist/packages/lib/src/with-auth.js.map +1 -0
- package/dist/packages/ui/src/config/create-side-nav.d.ts +6 -0
- package/dist/packages/ui/src/config/create-side-nav.d.ts.map +1 -0
- package/dist/packages/ui/src/config/create-side-nav.js +9 -0
- package/dist/packages/ui/src/config/create-side-nav.js.map +1 -0
- package/dist/packages/ui/src/feedback/confirmation.d.ts +16 -0
- package/dist/packages/ui/src/feedback/confirmation.d.ts.map +1 -0
- package/dist/packages/ui/src/feedback/confirmation.js +82 -0
- package/dist/packages/ui/src/feedback/confirmation.js.map +1 -0
- package/dist/packages/ui/src/feedback/error-display.d.ts +7 -0
- package/dist/packages/ui/src/feedback/error-display.d.ts.map +1 -0
- package/dist/packages/ui/src/feedback/error-display.js +11 -0
- package/dist/packages/ui/src/feedback/error-display.js.map +1 -0
- package/dist/packages/ui/src/forms/date-picker.d.ts +4 -0
- package/dist/packages/ui/src/forms/date-picker.d.ts.map +1 -0
- package/dist/packages/ui/src/forms/date-picker.js +9 -0
- package/dist/packages/ui/src/forms/date-picker.js.map +1 -0
- package/dist/packages/ui/src/forms/field-container.d.ts +8 -0
- package/dist/packages/ui/src/forms/field-container.d.ts.map +1 -0
- package/dist/packages/ui/src/forms/field-container.js +6 -0
- package/dist/packages/ui/src/forms/field-container.js.map +1 -0
- package/dist/packages/ui/src/forms/form.d.ts +9 -0
- package/dist/packages/ui/src/forms/form.d.ts.map +1 -0
- package/dist/packages/ui/src/forms/form.js +63 -0
- package/dist/packages/ui/src/forms/form.js.map +1 -0
- package/dist/packages/ui/src/forms/input.d.ts +4 -0
- package/dist/packages/ui/src/forms/input.d.ts.map +1 -0
- package/dist/packages/ui/src/forms/input.js +13 -0
- package/dist/packages/ui/src/forms/input.js.map +1 -0
- package/dist/packages/ui/src/forms/label.d.ts +7 -0
- package/dist/packages/ui/src/forms/label.d.ts.map +1 -0
- package/dist/packages/ui/src/forms/label.js +9 -0
- package/dist/packages/ui/src/forms/label.js.map +1 -0
- package/dist/packages/ui/src/forms/select.d.ts +6 -0
- package/dist/packages/ui/src/forms/select.d.ts.map +1 -0
- package/dist/packages/ui/src/forms/select.js +19 -0
- package/dist/packages/ui/src/forms/select.js.map +1 -0
- package/dist/packages/ui/src/forms/textarea.d.ts +4 -0
- package/dist/packages/ui/src/forms/textarea.d.ts.map +1 -0
- package/dist/packages/ui/src/forms/textarea.js +9 -0
- package/dist/packages/ui/src/forms/textarea.js.map +1 -0
- package/dist/packages/ui/src/headers/header-content.d.ts +11 -0
- package/dist/packages/ui/src/headers/header-content.d.ts.map +1 -0
- package/dist/packages/ui/src/headers/header-content.js +6 -0
- package/dist/packages/ui/src/headers/header-content.js.map +1 -0
- package/dist/packages/ui/src/illustrations/topic-illustration.d.ts +1 -0
- package/dist/packages/ui/src/illustrations/topic-illustration.d.ts.map +1 -0
- package/dist/packages/ui/src/illustrations/topic-illustration.js +69 -0
- package/dist/packages/ui/src/illustrations/topic-illustration.js.map +1 -0
- package/dist/packages/ui/src/index.d.ts +28 -0
- package/dist/packages/ui/src/index.d.ts.map +1 -0
- package/dist/packages/ui/src/index.js +38 -0
- package/dist/packages/ui/src/index.js.map +1 -0
- package/dist/packages/ui/src/layouts/side-nav.d.ts +45 -0
- package/dist/packages/ui/src/layouts/side-nav.d.ts.map +1 -0
- package/dist/packages/ui/src/layouts/side-nav.js +127 -0
- package/dist/packages/ui/src/layouts/side-nav.js.map +1 -0
- package/dist/packages/ui/src/loaders/loader-submit.d.ts +8 -0
- package/dist/packages/ui/src/loaders/loader-submit.d.ts.map +1 -0
- package/dist/packages/ui/src/loaders/loader-submit.js +7 -0
- package/dist/packages/ui/src/loaders/loader-submit.js.map +1 -0
- package/dist/packages/ui/src/loaders/loading.d.ts +6 -0
- package/dist/packages/ui/src/loaders/loading.d.ts.map +1 -0
- package/dist/packages/ui/src/loaders/loading.js +7 -0
- package/dist/packages/ui/src/loaders/loading.js.map +1 -0
- package/dist/packages/ui/src/modals/modal.d.ts +10 -0
- package/dist/packages/ui/src/modals/modal.d.ts.map +1 -0
- package/dist/packages/ui/src/modals/modal.js +36 -0
- package/dist/packages/ui/src/modals/modal.js.map +1 -0
- package/dist/packages/ui/src/pages/access-denied.d.ts +8 -0
- package/dist/packages/ui/src/pages/access-denied.d.ts.map +1 -0
- package/dist/packages/ui/src/pages/access-denied.js +8 -0
- package/dist/packages/ui/src/pages/access-denied.js.map +1 -0
- package/dist/packages/ui/src/pages/session-expired.d.ts +6 -0
- package/dist/packages/ui/src/pages/session-expired.d.ts.map +1 -0
- package/dist/packages/ui/src/pages/session-expired.js +21 -0
- package/dist/packages/ui/src/pages/session-expired.js.map +1 -0
- package/dist/packages/ui/src/pwa/InstallPrompt.d.ts +7 -0
- package/dist/packages/ui/src/pwa/InstallPrompt.d.ts.map +1 -0
- package/dist/packages/ui/src/pwa/InstallPrompt.js +71 -0
- package/dist/packages/ui/src/pwa/InstallPrompt.js.map +1 -0
- package/dist/packages/ui/src/pwa/SwRegistration.d.ts +5 -0
- package/dist/packages/ui/src/pwa/SwRegistration.d.ts.map +1 -0
- package/dist/packages/ui/src/pwa/SwRegistration.js +13 -0
- package/dist/packages/ui/src/pwa/SwRegistration.js.map +1 -0
- package/dist/packages/ui/src/route-guard/route-guard.d.ts +31 -0
- package/dist/packages/ui/src/route-guard/route-guard.d.ts.map +1 -0
- package/dist/packages/ui/src/route-guard/route-guard.js +75 -0
- package/dist/packages/ui/src/route-guard/route-guard.js.map +1 -0
- package/dist/packages/ui/src/search/input-search.d.ts +1 -0
- package/dist/packages/ui/src/search/input-search.d.ts.map +1 -0
- package/dist/packages/ui/src/search/input-search.js +47 -0
- package/dist/packages/ui/src/search/input-search.js.map +1 -0
- package/dist/packages/ui/src/search/select-search.d.ts +1 -0
- package/dist/packages/ui/src/search/select-search.d.ts.map +1 -0
- package/dist/packages/ui/src/search/select-search.js +100 -0
- package/dist/packages/ui/src/search/select-search.js.map +1 -0
- package/dist/packages/ui/src/table/actions-menu.d.ts +14 -0
- package/dist/packages/ui/src/table/actions-menu.d.ts.map +1 -0
- package/dist/packages/ui/src/table/actions-menu.js +53 -0
- package/dist/packages/ui/src/table/actions-menu.js.map +1 -0
- package/dist/packages/ui/src/table/table.d.ts +24 -0
- package/dist/packages/ui/src/table/table.d.ts.map +1 -0
- package/dist/packages/ui/src/table/table.js +99 -0
- package/dist/packages/ui/src/table/table.js.map +1 -0
- package/dist/packages/utils/src/fetcher.d.ts +16 -0
- package/dist/packages/utils/src/fetcher.d.ts.map +1 -0
- package/dist/packages/utils/src/fetcher.js +38 -0
- package/dist/packages/utils/src/fetcher.js.map +1 -0
- package/dist/packages/utils/src/formatCurrency.d.ts +1 -0
- package/dist/packages/utils/src/formatCurrency.d.ts.map +1 -0
- package/dist/packages/utils/src/formatCurrency.js +2 -0
- package/dist/packages/utils/src/formatCurrency.js.map +1 -0
- package/dist/packages/utils/src/formatDate.d.ts +1 -0
- package/dist/packages/utils/src/formatDate.d.ts.map +1 -0
- package/dist/packages/utils/src/formatDate.js +2 -0
- package/dist/packages/utils/src/formatDate.js.map +1 -0
- package/dist/packages/utils/src/index.d.ts +3 -0
- package/dist/packages/utils/src/index.d.ts.map +1 -0
- package/dist/packages/utils/src/index.js +2 -0
- package/dist/packages/utils/src/index.js.map +1 -0
- package/dist/src/client.d.ts +3 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +5 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/server.d.ts +2 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +3 -0
- package/dist/src/server.js.map +1 -0
- package/package.json +8 -8
- package/dist/client.d.ts +0 -210
- package/dist/client.js +0 -1407
- package/dist/client.js.map +0 -1
- package/dist/server.d.ts +0 -33
- package/dist/server.js +0 -161
- package/dist/server.js.map +0 -1
package/dist/client.js
DELETED
|
@@ -1,1407 +0,0 @@
|
|
|
1
|
-
// packages/ui/src/layouts/side-nav.tsx
|
|
2
|
-
import {
|
|
3
|
-
useState as useState2,
|
|
4
|
-
useEffect as useEffect3,
|
|
5
|
-
createElement,
|
|
6
|
-
isValidElement
|
|
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";
|
|
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";
|
|
246
|
-
function isActive(href, currentPath) {
|
|
247
|
-
return currentPath === href || currentPath.startsWith(href + "/");
|
|
248
|
-
}
|
|
249
|
-
function isExactActive(href, currentPath) {
|
|
250
|
-
return currentPath === href;
|
|
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
|
-
}
|
|
278
|
-
function SideNav({
|
|
279
|
-
children,
|
|
280
|
-
title,
|
|
281
|
-
mainItems,
|
|
282
|
-
currentPath,
|
|
283
|
-
brand,
|
|
284
|
-
footer,
|
|
285
|
-
toggleIcon
|
|
286
|
-
}) {
|
|
287
|
-
const activeMain = mainItems.find((item) => isActive(item.href, currentPath));
|
|
288
|
-
const activeTitleSource = findActiveTitleSource(mainItems, footer, currentPath);
|
|
289
|
-
const headerTitle = title ?? activeTitleSource?.title ?? activeTitleSource?.label ?? activeMain?.label;
|
|
290
|
-
const [expandedSection, setExpandedSection] = useState2(
|
|
291
|
-
activeMain?.id ?? null
|
|
292
|
-
);
|
|
293
|
-
const toggleSection = (id) => {
|
|
294
|
-
setExpandedSection((prev) => prev === id ? null : id);
|
|
295
|
-
};
|
|
296
|
-
const [isMobileOpen, setIsMobileOpen] = useState2(false);
|
|
297
|
-
useEffect3(() => {
|
|
298
|
-
const handleEscape = (e) => {
|
|
299
|
-
if (e.key === "Escape") setIsMobileOpen(false);
|
|
300
|
-
};
|
|
301
|
-
document.addEventListener("keydown", handleEscape);
|
|
302
|
-
return () => document.removeEventListener("keydown", handleEscape);
|
|
303
|
-
}, []);
|
|
304
|
-
useEffect3(() => {
|
|
305
|
-
if (isMobileOpen) {
|
|
306
|
-
document.body.style.overflow = "hidden";
|
|
307
|
-
} else {
|
|
308
|
-
document.body.style.overflow = "";
|
|
309
|
-
}
|
|
310
|
-
return () => {
|
|
311
|
-
document.body.style.overflow = "";
|
|
312
|
-
};
|
|
313
|
-
}, [isMobileOpen]);
|
|
314
|
-
const closeMobileNav = () => setIsMobileOpen(false);
|
|
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(
|
|
348
|
-
"aside",
|
|
349
|
-
{
|
|
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"}`,
|
|
351
|
-
children: [
|
|
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(
|
|
354
|
-
"img",
|
|
355
|
-
{
|
|
356
|
-
src: brand.logo,
|
|
357
|
-
alt: brand.name ?? "",
|
|
358
|
-
className: "w-12.5 h-12.5"
|
|
359
|
-
}
|
|
360
|
-
) : brand.logo),
|
|
361
|
-
brand.name && /* @__PURE__ */ jsx3("p", { className: "text-gray-500 text-base font-semibold", children: brand.name })
|
|
362
|
-
] }) }),
|
|
363
|
-
/* @__PURE__ */ jsx3("nav", { className: "flex-1 p-4 overflow-y-auto", children: /* @__PURE__ */ jsx3("ul", { className: "space-y-1", children: mainItems.map((item) => {
|
|
364
|
-
const active = isActive(item.href, currentPath);
|
|
365
|
-
const isExpanded = expandedSection === item.id;
|
|
366
|
-
const hasSubItems = item.subItems && item.subItems.length > 0;
|
|
367
|
-
return /* @__PURE__ */ jsxs3("li", { children: [
|
|
368
|
-
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
|
|
369
|
-
/* @__PURE__ */ jsxs3(
|
|
370
|
-
"a",
|
|
371
|
-
{
|
|
372
|
-
href: item.href,
|
|
373
|
-
onClick: closeMobileNav,
|
|
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"}`,
|
|
375
|
-
children: [
|
|
376
|
-
item.icon && (typeof item.icon === "string" ? /* @__PURE__ */ jsx3("span", { className: "text-lg", children: item.icon }) : isValidElement(item.icon) ? item.icon : createElement(
|
|
377
|
-
item.icon,
|
|
378
|
-
{ size: 20 }
|
|
379
|
-
)),
|
|
380
|
-
item.label
|
|
381
|
-
]
|
|
382
|
-
}
|
|
383
|
-
),
|
|
384
|
-
active && hasSubItems && /* @__PURE__ */ jsx3(
|
|
385
|
-
"button",
|
|
386
|
-
{
|
|
387
|
-
onClick: () => toggleSection(item.id),
|
|
388
|
-
className: `p-1.5 rounded-lg transition-colors ${active ? "text-black hover:bg-gray-100" : "text-gray-500 hover:bg-gray-100"}`,
|
|
389
|
-
title: isExpanded ? "R\xE9duire" : "D\xE9velopper",
|
|
390
|
-
children: toggleIcon ? /* @__PURE__ */ jsx3(
|
|
391
|
-
"span",
|
|
392
|
-
{
|
|
393
|
-
className: `inline-block transition-transform duration-200 ${isExpanded ? "rotate-0" : "-rotate-90"}`,
|
|
394
|
-
children: toggleIcon
|
|
395
|
-
}
|
|
396
|
-
) : /* @__PURE__ */ jsx3("span", { className: "text-xs", children: isExpanded ? "\u25BC" : "\u25B6" })
|
|
397
|
-
}
|
|
398
|
-
)
|
|
399
|
-
] }),
|
|
400
|
-
active && hasSubItems && /* @__PURE__ */ jsx3(
|
|
401
|
-
"div",
|
|
402
|
-
{
|
|
403
|
-
className: `grid transition-all duration-300 ease-in-out ${isExpanded ? "grid-rows-[1fr]" : "grid-rows-[0fr]"}`,
|
|
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(
|
|
405
|
-
"a",
|
|
406
|
-
{
|
|
407
|
-
href: sub.href,
|
|
408
|
-
onClick: closeMobileNav,
|
|
409
|
-
className: `flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors ${isExactActive(sub.href, currentPath) ? "bg-gray-200 text-black" : "text-gray-500 hover:bg-gray-100"}`,
|
|
410
|
-
children: sub.label
|
|
411
|
-
}
|
|
412
|
-
) }, sub.id)) }) })
|
|
413
|
-
}
|
|
414
|
-
)
|
|
415
|
-
] }, item.id);
|
|
416
|
-
}) }) }),
|
|
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
|
-
) })
|
|
444
|
-
]
|
|
445
|
-
}
|
|
446
|
-
),
|
|
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(
|
|
472
|
-
"div",
|
|
473
|
-
{
|
|
474
|
-
className: "fixed inset-0 z-40 bg-black/50 md:hidden",
|
|
475
|
-
onClick: closeMobileNav,
|
|
476
|
-
"aria-hidden": "true"
|
|
477
|
-
}
|
|
478
|
-
),
|
|
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(
|
|
483
|
-
"button",
|
|
484
|
-
{
|
|
485
|
-
className: "md:hidden p-2 -ml-2 rounded-lg hover:bg-gray-100 transition-colors",
|
|
486
|
-
onClick: () => setIsMobileOpen(!isMobileOpen),
|
|
487
|
-
"aria-label": isMobileOpen ? "Fermer le menu" : "Menu",
|
|
488
|
-
"aria-expanded": isMobileOpen,
|
|
489
|
-
children: isMobileOpen ? /* @__PURE__ */ jsx3(
|
|
490
|
-
"svg",
|
|
491
|
-
{
|
|
492
|
-
className: "w-5 h-5",
|
|
493
|
-
fill: "none",
|
|
494
|
-
viewBox: "0 0 24 24",
|
|
495
|
-
stroke: "currentColor",
|
|
496
|
-
strokeWidth: 2,
|
|
497
|
-
children: /* @__PURE__ */ jsx3(
|
|
498
|
-
"path",
|
|
499
|
-
{
|
|
500
|
-
strokeLinecap: "round",
|
|
501
|
-
strokeLinejoin: "round",
|
|
502
|
-
d: "M6 18L18 6M6 6l12 12"
|
|
503
|
-
}
|
|
504
|
-
)
|
|
505
|
-
}
|
|
506
|
-
) : /* @__PURE__ */ jsx3(
|
|
507
|
-
"svg",
|
|
508
|
-
{
|
|
509
|
-
className: "w-5 h-5",
|
|
510
|
-
fill: "none",
|
|
511
|
-
viewBox: "0 0 24 24",
|
|
512
|
-
stroke: "currentColor",
|
|
513
|
-
strokeWidth: 2,
|
|
514
|
-
children: /* @__PURE__ */ jsx3(
|
|
515
|
-
"path",
|
|
516
|
-
{
|
|
517
|
-
strokeLinecap: "round",
|
|
518
|
-
strokeLinejoin: "round",
|
|
519
|
-
d: "M4 6h16M4 12h16M4 18h16"
|
|
520
|
-
}
|
|
521
|
-
)
|
|
522
|
-
}
|
|
523
|
-
)
|
|
524
|
-
}
|
|
525
|
-
),
|
|
526
|
-
/* @__PURE__ */ jsx3("h1", { className: "text-base font-semibold text-gray-500", children: headerTitle })
|
|
527
|
-
] }),
|
|
528
|
-
brand?.tagline && /* @__PURE__ */ jsx3("span", { className: "text-sm text-gray-500 max-md:hidden", children: brand.tagline })
|
|
529
|
-
] }),
|
|
530
|
-
!headerTitle && /* @__PURE__ */ jsx3(
|
|
531
|
-
"button",
|
|
532
|
-
{
|
|
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",
|
|
534
|
-
onClick: () => setIsMobileOpen(!isMobileOpen),
|
|
535
|
-
"aria-label": isMobileOpen ? "Fermer le menu" : "Menu",
|
|
536
|
-
"aria-expanded": isMobileOpen,
|
|
537
|
-
children: isMobileOpen ? /* @__PURE__ */ jsx3(
|
|
538
|
-
"svg",
|
|
539
|
-
{
|
|
540
|
-
className: "w-5 h-5",
|
|
541
|
-
fill: "none",
|
|
542
|
-
viewBox: "0 0 24 24",
|
|
543
|
-
stroke: "currentColor",
|
|
544
|
-
strokeWidth: 2,
|
|
545
|
-
children: /* @__PURE__ */ jsx3(
|
|
546
|
-
"path",
|
|
547
|
-
{
|
|
548
|
-
strokeLinecap: "round",
|
|
549
|
-
strokeLinejoin: "round",
|
|
550
|
-
d: "M6 18L18 6M6 6l12 12"
|
|
551
|
-
}
|
|
552
|
-
)
|
|
553
|
-
}
|
|
554
|
-
) : /* @__PURE__ */ jsx3(
|
|
555
|
-
"svg",
|
|
556
|
-
{
|
|
557
|
-
className: "w-5 h-5",
|
|
558
|
-
fill: "none",
|
|
559
|
-
viewBox: "0 0 24 24",
|
|
560
|
-
stroke: "currentColor",
|
|
561
|
-
strokeWidth: 2,
|
|
562
|
-
children: /* @__PURE__ */ jsx3(
|
|
563
|
-
"path",
|
|
564
|
-
{
|
|
565
|
-
strokeLinecap: "round",
|
|
566
|
-
strokeLinejoin: "round",
|
|
567
|
-
d: "M4 6h16M4 12h16M4 18h16"
|
|
568
|
-
}
|
|
569
|
-
)
|
|
570
|
-
}
|
|
571
|
-
)
|
|
572
|
-
}
|
|
573
|
-
),
|
|
574
|
-
/* @__PURE__ */ jsx3(
|
|
575
|
-
"main",
|
|
576
|
-
{
|
|
577
|
-
className: (headerTitle ? "p-6 pt-20" : "p-6") + " overflow-x-auto",
|
|
578
|
-
children
|
|
579
|
-
}
|
|
580
|
-
)
|
|
581
|
-
] })
|
|
582
|
-
] });
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// packages/ui/src/config/create-side-nav.tsx
|
|
586
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
587
|
-
function createSideNav(config) {
|
|
588
|
-
return function ConfiguredSideNav({
|
|
589
|
-
children,
|
|
590
|
-
currentPath
|
|
591
|
-
}) {
|
|
592
|
-
return /* @__PURE__ */ jsx4(SideNav, { ...config, currentPath, children });
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
// packages/ui/src/table/table.tsx
|
|
597
|
-
import { Printer, ChevronLeft, ChevronRight } from "lucide-react";
|
|
598
|
-
import clsx from "clsx";
|
|
599
|
-
import { useState as useState4, useMemo, useCallback as useCallback2 } from "react";
|
|
600
|
-
|
|
601
|
-
// packages/ui/src/feedback/error-display.tsx
|
|
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";
|
|
605
|
-
function ErrorDisplay({ error }) {
|
|
606
|
-
const [dismissed, setDismissed] = useState3(false);
|
|
607
|
-
if (!error || dismissed) return null;
|
|
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 })
|
|
612
|
-
] }),
|
|
613
|
-
/* @__PURE__ */ jsx5(
|
|
614
|
-
"button",
|
|
615
|
-
{
|
|
616
|
-
onClick: () => setDismissed(true),
|
|
617
|
-
className: "text-red-400 hover:text-red-600 shrink-0 cursor-pointer",
|
|
618
|
-
children: /* @__PURE__ */ jsx5(X2, { className: "w-5 h-5" })
|
|
619
|
-
}
|
|
620
|
-
)
|
|
621
|
-
] }) }) });
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// packages/ui/src/table/table.tsx
|
|
625
|
-
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
626
|
-
var VISIBLE_PAGES = 5;
|
|
627
|
-
function Table({
|
|
628
|
-
data,
|
|
629
|
-
columns,
|
|
630
|
-
loading,
|
|
631
|
-
error,
|
|
632
|
-
label,
|
|
633
|
-
printable,
|
|
634
|
-
pageSize = 5,
|
|
635
|
-
pageSizeOptions = [5, 10, 20],
|
|
636
|
-
showPagination = true
|
|
637
|
-
}) {
|
|
638
|
-
const [tooltip, setTooltip] = useState4(null);
|
|
639
|
-
const [page, setPage] = useState4(1);
|
|
640
|
-
const [perPage, setPerPage] = useState4(pageSize);
|
|
641
|
-
const totalPages = useMemo(
|
|
642
|
-
() => Math.max(1, Math.ceil(data.length / perPage)),
|
|
643
|
-
[data.length, perPage]
|
|
644
|
-
);
|
|
645
|
-
const safePage = Math.min(page, totalPages);
|
|
646
|
-
const displayData = useMemo(
|
|
647
|
-
() => data.slice((safePage - 1) * perPage, safePage * perPage),
|
|
648
|
-
[data, safePage, perPage]
|
|
649
|
-
);
|
|
650
|
-
const paginationRange = useMemo(() => {
|
|
651
|
-
const half = Math.floor(VISIBLE_PAGES / 2);
|
|
652
|
-
let start = Math.max(1, safePage - half);
|
|
653
|
-
let end = Math.min(totalPages, start + VISIBLE_PAGES - 1);
|
|
654
|
-
if (end - start + 1 < VISIBLE_PAGES) {
|
|
655
|
-
start = Math.max(1, end - VISIBLE_PAGES + 1);
|
|
656
|
-
}
|
|
657
|
-
const range = [];
|
|
658
|
-
if (start > 1) {
|
|
659
|
-
range.push(1);
|
|
660
|
-
if (start > 2) range.push("...");
|
|
661
|
-
}
|
|
662
|
-
for (let i = start; i <= end; i++) range.push(i);
|
|
663
|
-
if (end < totalPages) {
|
|
664
|
-
if (end < totalPages - 1) range.push("...");
|
|
665
|
-
range.push(totalPages);
|
|
666
|
-
}
|
|
667
|
-
return range;
|
|
668
|
-
}, [safePage, totalPages]);
|
|
669
|
-
const handlePageSizeChange = useCallback2((value) => {
|
|
670
|
-
setPerPage(value);
|
|
671
|
-
setPage(1);
|
|
672
|
-
}, []);
|
|
673
|
-
const colCount = columns.length;
|
|
674
|
-
const colWidth = colCount > 0 ? `${Math.round(100 / colCount)}%` : "auto";
|
|
675
|
-
const handleMouseEnter = (e, content) => {
|
|
676
|
-
const rect = e.currentTarget.getBoundingClientRect();
|
|
677
|
-
setTooltip({
|
|
678
|
-
content,
|
|
679
|
-
x: rect.left + rect.width / 2,
|
|
680
|
-
y: rect.top
|
|
681
|
-
});
|
|
682
|
-
};
|
|
683
|
-
const handleMouseLeave = () => {
|
|
684
|
-
setTooltip(null);
|
|
685
|
-
};
|
|
686
|
-
const banner = error ? /* @__PURE__ */ jsx6("div", { className: "mb-4", children: /* @__PURE__ */ jsx6(ErrorDisplay, { error }, error.message) }) : null;
|
|
687
|
-
if (loading) {
|
|
688
|
-
return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
|
|
689
|
-
banner,
|
|
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..." }) })
|
|
691
|
-
] });
|
|
692
|
-
}
|
|
693
|
-
if (!data || data.length === 0) {
|
|
694
|
-
return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
|
|
695
|
-
banner,
|
|
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" }) })
|
|
697
|
-
] });
|
|
698
|
-
}
|
|
699
|
-
return /* @__PURE__ */ jsxs5("div", { className: "w-full overflow-x-auto", children: [
|
|
700
|
-
banner,
|
|
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: [
|
|
703
|
-
label,
|
|
704
|
-
": ",
|
|
705
|
-
data.length
|
|
706
|
-
] }),
|
|
707
|
-
printable && /* @__PURE__ */ jsxs5(
|
|
708
|
-
"button",
|
|
709
|
-
{
|
|
710
|
-
onClick: () => window.print(),
|
|
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",
|
|
712
|
-
children: [
|
|
713
|
-
/* @__PURE__ */ jsx6(Printer, { className: "w-4 h-4" }),
|
|
714
|
-
"Imprimer"
|
|
715
|
-
]
|
|
716
|
-
}
|
|
717
|
-
)
|
|
718
|
-
] }),
|
|
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(
|
|
721
|
-
"th",
|
|
722
|
-
{
|
|
723
|
-
style: { width: colWidth },
|
|
724
|
-
className: clsx(
|
|
725
|
-
"px-6 py-3 text-left text-xs font-bold text-white uppercase tracking-wider truncate",
|
|
726
|
-
col.excludeFromPrint && "print:hidden"
|
|
727
|
-
),
|
|
728
|
-
children: col.header
|
|
729
|
-
},
|
|
730
|
-
index
|
|
731
|
-
)) }) }),
|
|
732
|
-
/* @__PURE__ */ jsx6(
|
|
733
|
-
"tbody",
|
|
734
|
-
{
|
|
735
|
-
className: clsx(
|
|
736
|
-
"bg-white divide-y divide-gray-200",
|
|
737
|
-
printable && "print:hidden"
|
|
738
|
-
),
|
|
739
|
-
children: displayData.map((row, rowIndex) => /* @__PURE__ */ jsx6(
|
|
740
|
-
"tr",
|
|
741
|
-
{
|
|
742
|
-
className: "hover:bg-gray-100 transition-colors duration-200",
|
|
743
|
-
children: columns.map((col, colIndex) => {
|
|
744
|
-
const cellContent = col.render ? col.render(row) : String(row[col.accessor]);
|
|
745
|
-
const textContent = typeof cellContent === "string" ? cellContent : "";
|
|
746
|
-
return /* @__PURE__ */ jsx6(
|
|
747
|
-
"td",
|
|
748
|
-
{
|
|
749
|
-
style: { width: colWidth },
|
|
750
|
-
className: clsx(
|
|
751
|
-
"px-6 py-4 text-sm text-gray-700",
|
|
752
|
-
col.excludeFromPrint && "print:hidden"
|
|
753
|
-
),
|
|
754
|
-
children: typeof cellContent === "string" ? /* @__PURE__ */ jsx6(
|
|
755
|
-
"div",
|
|
756
|
-
{
|
|
757
|
-
className: "truncate",
|
|
758
|
-
onMouseEnter: (e) => handleMouseEnter(e, textContent),
|
|
759
|
-
onMouseLeave: handleMouseLeave,
|
|
760
|
-
children: cellContent
|
|
761
|
-
}
|
|
762
|
-
) : cellContent
|
|
763
|
-
},
|
|
764
|
-
colIndex
|
|
765
|
-
);
|
|
766
|
-
})
|
|
767
|
-
},
|
|
768
|
-
rowIndex
|
|
769
|
-
))
|
|
770
|
-
}
|
|
771
|
-
),
|
|
772
|
-
printable && /* @__PURE__ */ jsx6("tbody", { className: "hidden print:table-row-group", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx6("tr", { children: columns.map((col, colIndex) => {
|
|
773
|
-
const cellContent = col.render ? col.render(row) : String(row[col.accessor]);
|
|
774
|
-
return /* @__PURE__ */ jsx6(
|
|
775
|
-
"td",
|
|
776
|
-
{
|
|
777
|
-
style: { width: colWidth },
|
|
778
|
-
className: clsx(
|
|
779
|
-
"px-6 py-4 text-sm text-gray-700",
|
|
780
|
-
col.excludeFromPrint && "print:hidden"
|
|
781
|
-
),
|
|
782
|
-
children: cellContent
|
|
783
|
-
},
|
|
784
|
-
colIndex
|
|
785
|
-
);
|
|
786
|
-
}) }, rowIndex)) })
|
|
787
|
-
] }) }),
|
|
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(
|
|
792
|
-
"select",
|
|
793
|
-
{
|
|
794
|
-
value: perPage,
|
|
795
|
-
onChange: (e) => handlePageSizeChange(Number(e.target.value)),
|
|
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",
|
|
797
|
-
children: pageSizeOptions.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt, children: opt }, opt))
|
|
798
|
-
}
|
|
799
|
-
)
|
|
800
|
-
] }),
|
|
801
|
-
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-1", children: [
|
|
802
|
-
/* @__PURE__ */ jsx6(
|
|
803
|
-
"button",
|
|
804
|
-
{
|
|
805
|
-
onClick: () => setPage((p) => Math.max(1, p - 1)),
|
|
806
|
-
disabled: safePage <= 1,
|
|
807
|
-
className: clsx(
|
|
808
|
-
"p-1.5 rounded transition-colors",
|
|
809
|
-
safePage <= 1 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"
|
|
810
|
-
),
|
|
811
|
-
children: /* @__PURE__ */ jsx6(ChevronLeft, { className: "w-4 h-4" })
|
|
812
|
-
}
|
|
813
|
-
),
|
|
814
|
-
paginationRange.map(
|
|
815
|
-
(item, i) => item === "..." ? /* @__PURE__ */ jsx6("span", { className: "px-2 text-gray-400", children: "..." }, `ellipsis-${i}`) : /* @__PURE__ */ jsx6(
|
|
816
|
-
"button",
|
|
817
|
-
{
|
|
818
|
-
onClick: () => setPage(item),
|
|
819
|
-
className: clsx(
|
|
820
|
-
"min-w-8 px-2 py-1 rounded text-sm font-medium transition-colors",
|
|
821
|
-
item === safePage ? "bg-blue-500 text-white" : "text-gray-600 hover:bg-gray-100"
|
|
822
|
-
),
|
|
823
|
-
children: item
|
|
824
|
-
},
|
|
825
|
-
item
|
|
826
|
-
)
|
|
827
|
-
),
|
|
828
|
-
/* @__PURE__ */ jsx6(
|
|
829
|
-
"button",
|
|
830
|
-
{
|
|
831
|
-
onClick: () => setPage((p) => Math.min(totalPages, p + 1)),
|
|
832
|
-
disabled: safePage >= totalPages,
|
|
833
|
-
className: clsx(
|
|
834
|
-
"p-1.5 rounded transition-colors",
|
|
835
|
-
safePage >= totalPages ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"
|
|
836
|
-
),
|
|
837
|
-
children: /* @__PURE__ */ jsx6(ChevronRight, { className: "w-4 h-4" })
|
|
838
|
-
}
|
|
839
|
-
)
|
|
840
|
-
] }),
|
|
841
|
-
/* @__PURE__ */ jsxs5("div", { className: "text-gray-500", children: [
|
|
842
|
-
"Page ",
|
|
843
|
-
safePage,
|
|
844
|
-
" sur ",
|
|
845
|
-
totalPages
|
|
846
|
-
] })
|
|
847
|
-
] }),
|
|
848
|
-
tooltip && /* @__PURE__ */ jsx6(
|
|
849
|
-
"div",
|
|
850
|
-
{
|
|
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",
|
|
852
|
-
style: {
|
|
853
|
-
left: tooltip.x,
|
|
854
|
-
top: tooltip.y - 8,
|
|
855
|
-
transform: "translate(-50%, -100%)"
|
|
856
|
-
},
|
|
857
|
-
children: tooltip.content
|
|
858
|
-
}
|
|
859
|
-
),
|
|
860
|
-
/* @__PURE__ */ jsx6("style", { children: `
|
|
861
|
-
@media print {
|
|
862
|
-
body * { visibility: hidden; }
|
|
863
|
-
.w-full.overflow-x-auto { visibility: visible; }
|
|
864
|
-
.w-full.overflow-x-auto * { visibility: visible; }
|
|
865
|
-
.w-full.overflow-x-auto { position: absolute; left: 0; top: 0; width: 100%; }
|
|
866
|
-
}
|
|
867
|
-
` })
|
|
868
|
-
] });
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
// packages/ui/src/table/actions-menu.tsx
|
|
872
|
-
import { useState as useState5, useRef as useRef2, useEffect as useEffect4 } from "react";
|
|
873
|
-
import { Ellipsis } from "lucide-react";
|
|
874
|
-
import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
875
|
-
function ActionsMenu({ row, actions }) {
|
|
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(() => {
|
|
881
|
-
function handleClickOutside(event) {
|
|
882
|
-
if (menuRef.current && buttonRef.current && !menuRef.current.contains(event.target) && !buttonRef.current.contains(event.target)) {
|
|
883
|
-
setIsOpen(false);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
887
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
888
|
-
}, []);
|
|
889
|
-
const toggleMenu = () => {
|
|
890
|
-
if (buttonRef.current) {
|
|
891
|
-
const rect = buttonRef.current.getBoundingClientRect();
|
|
892
|
-
const menuWidth = 180;
|
|
893
|
-
const menuHeight = actions.length * 40;
|
|
894
|
-
const spaceBelow = window.innerHeight - rect.bottom;
|
|
895
|
-
const spaceRight = window.innerWidth - rect.left;
|
|
896
|
-
let top = rect.bottom;
|
|
897
|
-
let left = rect.left;
|
|
898
|
-
if (spaceBelow < menuHeight + 20) {
|
|
899
|
-
top = rect.top - menuHeight;
|
|
900
|
-
}
|
|
901
|
-
if (spaceRight < menuWidth + 20) {
|
|
902
|
-
left = rect.left - menuWidth + 30;
|
|
903
|
-
}
|
|
904
|
-
setCoords({ top, left });
|
|
905
|
-
}
|
|
906
|
-
setIsOpen(!isOpen);
|
|
907
|
-
};
|
|
908
|
-
return /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
909
|
-
/* @__PURE__ */ jsx7(
|
|
910
|
-
"button",
|
|
911
|
-
{
|
|
912
|
-
ref: buttonRef,
|
|
913
|
-
onClick: toggleMenu,
|
|
914
|
-
className: "p-1 hover:bg-gray-100 rounded-full",
|
|
915
|
-
children: /* @__PURE__ */ jsx7(Ellipsis, { className: "w-5 h-5" })
|
|
916
|
-
}
|
|
917
|
-
),
|
|
918
|
-
isOpen && /* @__PURE__ */ jsx7(
|
|
919
|
-
"div",
|
|
920
|
-
{
|
|
921
|
-
ref: menuRef,
|
|
922
|
-
className: "fixed bg-white border border-gray-200 rounded shadow-lg z-20",
|
|
923
|
-
style: {
|
|
924
|
-
top: coords.top,
|
|
925
|
-
left: coords.left,
|
|
926
|
-
minWidth: 180
|
|
927
|
-
},
|
|
928
|
-
children: actions.map((action, idx) => /* @__PURE__ */ jsxs6(
|
|
929
|
-
"button",
|
|
930
|
-
{
|
|
931
|
-
className: `flex items-center gap-2 px-4 py-2 w-full text-sm hover:bg-gray-100 ${action.color || "text-gray-700"}`,
|
|
932
|
-
onClick: () => {
|
|
933
|
-
action.callback(row);
|
|
934
|
-
setIsOpen(false);
|
|
935
|
-
},
|
|
936
|
-
children: [
|
|
937
|
-
action.icon,
|
|
938
|
-
action.label
|
|
939
|
-
]
|
|
940
|
-
},
|
|
941
|
-
idx
|
|
942
|
-
))
|
|
943
|
-
}
|
|
944
|
-
)
|
|
945
|
-
] });
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
// packages/ui/src/headers/header-content.tsx
|
|
949
|
-
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
950
|
-
function HeaderContent({
|
|
951
|
-
title,
|
|
952
|
-
description,
|
|
953
|
-
actionLabel,
|
|
954
|
-
onAction,
|
|
955
|
-
icon
|
|
956
|
-
}) {
|
|
957
|
-
return /* @__PURE__ */ jsxs7("div", { className: "flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-6", children: [
|
|
958
|
-
/* @__PURE__ */ jsxs7("div", { children: [
|
|
959
|
-
/* @__PURE__ */ jsx8("h1", { className: "text-xl font-semibold text-gray-900 flex items-center gap-2", children: title }),
|
|
960
|
-
description && /* @__PURE__ */ jsx8("p", { className: "text-sm text-gray-500 mt-1", children: description })
|
|
961
|
-
] }),
|
|
962
|
-
actionLabel && onAction && /* @__PURE__ */ jsxs7(
|
|
963
|
-
"button",
|
|
964
|
-
{
|
|
965
|
-
onClick: onAction,
|
|
966
|
-
className: "font-semibold md:inline-flex items-center gap-2 bg-gray-900 text-white px-4 py-2 rounded-md text-sm hover:bg-gray-700 transition hover:cursor-pointer",
|
|
967
|
-
children: [
|
|
968
|
-
icon && /* @__PURE__ */ jsx8("span", { className: "w-4 h-4", children: icon }),
|
|
969
|
-
actionLabel
|
|
970
|
-
]
|
|
971
|
-
}
|
|
972
|
-
)
|
|
973
|
-
] });
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
// packages/ui/src/forms/form.tsx
|
|
977
|
-
import { useState as useState6, useEffect as useEffect5, useRef as useRef3, useCallback as useCallback3 } from "react";
|
|
978
|
-
import { X as X3 } from "lucide-react";
|
|
979
|
-
import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
980
|
-
var Form = ({
|
|
981
|
-
erreurs,
|
|
982
|
-
onErrorClose,
|
|
983
|
-
children,
|
|
984
|
-
className,
|
|
985
|
-
...props
|
|
986
|
-
}) => {
|
|
987
|
-
const formClassName = ["flex flex-col gap-3", className].filter(Boolean).join(" ");
|
|
988
|
-
const errorMessage = erreurs?.message || null;
|
|
989
|
-
const [showError, setShowError] = useState6(!!erreurs);
|
|
990
|
-
const [countdown, setCountdown] = useState6(erreurs ? 10 : 0);
|
|
991
|
-
const [animating, setAnimating] = useState6(!!erreurs);
|
|
992
|
-
const intervalRef = useRef3(null);
|
|
993
|
-
const hasClosedRef = useRef3(false);
|
|
994
|
-
const closeError = useCallback3(
|
|
995
|
-
(triggerCallback = true) => {
|
|
996
|
-
if (intervalRef.current !== null) {
|
|
997
|
-
window.clearInterval(intervalRef.current);
|
|
998
|
-
intervalRef.current = null;
|
|
999
|
-
}
|
|
1000
|
-
setShowError(false);
|
|
1001
|
-
setAnimating(false);
|
|
1002
|
-
setCountdown(0);
|
|
1003
|
-
if (triggerCallback && onErrorClose && !hasClosedRef.current) {
|
|
1004
|
-
hasClosedRef.current = true;
|
|
1005
|
-
onErrorClose();
|
|
1006
|
-
}
|
|
1007
|
-
},
|
|
1008
|
-
[onErrorClose]
|
|
1009
|
-
);
|
|
1010
|
-
useEffect5(() => {
|
|
1011
|
-
hasClosedRef.current = false;
|
|
1012
|
-
if (!erreurs) {
|
|
1013
|
-
const closeTimeout = window.setTimeout(() => closeError(false), 0);
|
|
1014
|
-
return () => {
|
|
1015
|
-
window.clearTimeout(closeTimeout);
|
|
1016
|
-
};
|
|
1017
|
-
}
|
|
1018
|
-
const initTimeout = window.setTimeout(() => {
|
|
1019
|
-
setShowError(true);
|
|
1020
|
-
setAnimating(true);
|
|
1021
|
-
setCountdown(10);
|
|
1022
|
-
}, 0);
|
|
1023
|
-
intervalRef.current = window.setInterval(() => {
|
|
1024
|
-
setCountdown((prev) => {
|
|
1025
|
-
if (prev <= 1) {
|
|
1026
|
-
closeError(true);
|
|
1027
|
-
return 0;
|
|
1028
|
-
}
|
|
1029
|
-
return prev - 1;
|
|
1030
|
-
});
|
|
1031
|
-
}, 1e3);
|
|
1032
|
-
return () => {
|
|
1033
|
-
window.clearTimeout(initTimeout);
|
|
1034
|
-
if (intervalRef.current !== null) {
|
|
1035
|
-
window.clearInterval(intervalRef.current);
|
|
1036
|
-
intervalRef.current = null;
|
|
1037
|
-
}
|
|
1038
|
-
};
|
|
1039
|
-
}, [erreurs, closeError]);
|
|
1040
|
-
return /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
1041
|
-
/* @__PURE__ */ jsx9("form", { className: formClassName, ...props, children }),
|
|
1042
|
-
showError && errorMessage && /* @__PURE__ */ jsxs8(
|
|
1043
|
-
"div",
|
|
1044
|
-
{
|
|
1045
|
-
className: `fixed top-0 left-0 right-0 z-[9999] bg-red-500 text-white p-4 rounded-b-md shadow-lg flex justify-between items-center transition-transform duration-300 ${animating ? "transform translate-y-0" : "transform -translate-y-full"}`,
|
|
1046
|
-
children: [
|
|
1047
|
-
/* @__PURE__ */ jsx9("span", { children: errorMessage }),
|
|
1048
|
-
/* @__PURE__ */ jsxs8("div", { className: "flex items-center", children: [
|
|
1049
|
-
/* @__PURE__ */ jsxs8("span", { className: "mr-2", children: [
|
|
1050
|
-
countdown,
|
|
1051
|
-
"s"
|
|
1052
|
-
] }),
|
|
1053
|
-
/* @__PURE__ */ jsx9("button", { onClick: () => closeError(true), children: /* @__PURE__ */ jsx9(X3, { size: 20 }) })
|
|
1054
|
-
] })
|
|
1055
|
-
]
|
|
1056
|
-
}
|
|
1057
|
-
)
|
|
1058
|
-
] });
|
|
1059
|
-
};
|
|
1060
|
-
|
|
1061
|
-
// packages/ui/src/forms/field-container.tsx
|
|
1062
|
-
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1063
|
-
function FieldContainer({ children, className }) {
|
|
1064
|
-
return /* @__PURE__ */ jsx10("div", { className: `flex flex-col gap-2 mb-3 relative ${className ?? ""}`, children: /* @__PURE__ */ jsx10("div", { className: "flex flex-col gap-1", children }) });
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
// packages/ui/src/forms/label.tsx
|
|
1068
|
-
import React4 from "react";
|
|
1069
|
-
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1070
|
-
var Label = React4.forwardRef(
|
|
1071
|
-
({ required, children, className, ...props }, ref) => {
|
|
1072
|
-
return /* @__PURE__ */ jsxs9(
|
|
1073
|
-
"label",
|
|
1074
|
-
{
|
|
1075
|
-
ref,
|
|
1076
|
-
className: `font-semibold text-gray-600 block ${className ?? ""}`.trim(),
|
|
1077
|
-
...props,
|
|
1078
|
-
children: [
|
|
1079
|
-
children,
|
|
1080
|
-
required ? /* @__PURE__ */ jsx11("span", { className: "text-red-500 ml-1", children: "*" }) : null
|
|
1081
|
-
]
|
|
1082
|
-
}
|
|
1083
|
-
);
|
|
1084
|
-
}
|
|
1085
|
-
);
|
|
1086
|
-
Label.displayName = "Label";
|
|
1087
|
-
|
|
1088
|
-
// packages/ui/src/forms/input.tsx
|
|
1089
|
-
import React5, { useState as useState7 } from "react";
|
|
1090
|
-
import { Eye, EyeOff } from "lucide-react";
|
|
1091
|
-
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1092
|
-
var Input = React5.forwardRef(({ type, className, ...props }, ref) => {
|
|
1093
|
-
const [showPassword, setShowPassword] = useState7(false);
|
|
1094
|
-
const isPassword = type === "password";
|
|
1095
|
-
const inputType = isPassword ? showPassword ? "text" : "password" : type;
|
|
1096
|
-
return /* @__PURE__ */ jsxs10("div", { className: "relative", children: [
|
|
1097
|
-
/* @__PURE__ */ jsx12(
|
|
1098
|
-
"input",
|
|
1099
|
-
{
|
|
1100
|
-
ref,
|
|
1101
|
-
type: inputType,
|
|
1102
|
-
className: `w-full px-3 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500 focus:border-transparent transition duration-200 ${className || ""}`,
|
|
1103
|
-
...props
|
|
1104
|
-
}
|
|
1105
|
-
),
|
|
1106
|
-
isPassword && /* @__PURE__ */ jsx12(
|
|
1107
|
-
"button",
|
|
1108
|
-
{
|
|
1109
|
-
type: "button",
|
|
1110
|
-
onClick: () => setShowPassword(!showPassword),
|
|
1111
|
-
className: "absolute inset-y-0 right-0 pr-3 flex items-center text-gray-500 hover:text-gray-700",
|
|
1112
|
-
children: showPassword ? /* @__PURE__ */ jsx12(EyeOff, { size: 20 }) : /* @__PURE__ */ jsx12(Eye, { size: 20 })
|
|
1113
|
-
}
|
|
1114
|
-
)
|
|
1115
|
-
] });
|
|
1116
|
-
});
|
|
1117
|
-
Input.displayName = "Input";
|
|
1118
|
-
|
|
1119
|
-
// packages/ui/src/forms/select.tsx
|
|
1120
|
-
import React6 from "react";
|
|
1121
|
-
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1122
|
-
var Select = React6.forwardRef(({ className, ...props }, ref) => {
|
|
1123
|
-
return /* @__PURE__ */ jsx13(
|
|
1124
|
-
"select",
|
|
1125
|
-
{
|
|
1126
|
-
ref,
|
|
1127
|
-
className: `w-full px-3 py-2 pr-10 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500 focus:border-transparent transition duration-200 bg-white cursor-pointer appearance-none bg-no-repeat bg-right bg-contain ${className || ""}`,
|
|
1128
|
-
style: {
|
|
1129
|
-
backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e")`,
|
|
1130
|
-
backgroundPosition: "right 0.75rem center",
|
|
1131
|
-
backgroundSize: "1.25em 1.25em"
|
|
1132
|
-
},
|
|
1133
|
-
...props
|
|
1134
|
-
}
|
|
1135
|
-
);
|
|
1136
|
-
});
|
|
1137
|
-
Select.displayName = "Select";
|
|
1138
|
-
var Option = React6.forwardRef(({ ...props }, ref) => {
|
|
1139
|
-
return /* @__PURE__ */ jsx13("option", { ref, ...props });
|
|
1140
|
-
});
|
|
1141
|
-
Option.displayName = "Option";
|
|
1142
|
-
|
|
1143
|
-
// packages/ui/src/forms/textarea.tsx
|
|
1144
|
-
import React7 from "react";
|
|
1145
|
-
import { jsx as jsx14 } from "react/jsx-runtime";
|
|
1146
|
-
var TextArea = React7.forwardRef(({ ...props }, ref) => {
|
|
1147
|
-
return /* @__PURE__ */ jsx14(
|
|
1148
|
-
"textarea",
|
|
1149
|
-
{
|
|
1150
|
-
ref,
|
|
1151
|
-
className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500 focus:border-transparent transition duration-200 resize-none",
|
|
1152
|
-
...props
|
|
1153
|
-
}
|
|
1154
|
-
);
|
|
1155
|
-
});
|
|
1156
|
-
TextArea.displayName = "TextArea";
|
|
1157
|
-
|
|
1158
|
-
// packages/ui/src/forms/date-picker.tsx
|
|
1159
|
-
import React8 from "react";
|
|
1160
|
-
import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1161
|
-
var DatePicker = React8.forwardRef(({ className, placeholder, required, ...props }, ref) => {
|
|
1162
|
-
return /* @__PURE__ */ jsxs11("div", { className: "relative", children: [
|
|
1163
|
-
/* @__PURE__ */ jsx15(
|
|
1164
|
-
"input",
|
|
1165
|
-
{
|
|
1166
|
-
ref,
|
|
1167
|
-
type: "date",
|
|
1168
|
-
required: required ?? true,
|
|
1169
|
-
className: `peer w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-gray-500 focus:border-transparent transition duration-200 ${className ?? ""}`,
|
|
1170
|
-
...props
|
|
1171
|
-
}
|
|
1172
|
-
),
|
|
1173
|
-
/* @__PURE__ */ jsx15("span", { className: "absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 pointer-events-none peer-valid:hidden md:hidden", children: placeholder ?? "jj/mm/aaaa" })
|
|
1174
|
-
] });
|
|
1175
|
-
});
|
|
1176
|
-
DatePicker.displayName = "DatePicker";
|
|
1177
|
-
|
|
1178
|
-
// packages/ui/src/loaders/loading.tsx
|
|
1179
|
-
import { Loader } from "lucide-react";
|
|
1180
|
-
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1181
|
-
function Loading(props) {
|
|
1182
|
-
return /* @__PURE__ */ jsx16("div", { className: "flex items-center justify-center", children: /* @__PURE__ */ jsxs12("div", { className: "flex flex-col items-center gap-4", children: [
|
|
1183
|
-
/* @__PURE__ */ jsx16(Loader, { className: "animate-spin", size: 40 }),
|
|
1184
|
-
/* @__PURE__ */ jsx16("p", { className: "text-gray-600 text-md font-medium", children: props.text })
|
|
1185
|
-
] }) });
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
// packages/ui/src/loaders/loader-submit.tsx
|
|
1189
|
-
import { LoaderCircle } from "lucide-react";
|
|
1190
|
-
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1191
|
-
function LoaderSubmit({ label, loadingLabel, isLoading }) {
|
|
1192
|
-
return /* @__PURE__ */ jsx17(Fragment3, { children: isLoading ? /* @__PURE__ */ jsxs13("div", { className: "flex flex-row gap-2 items-center", children: [
|
|
1193
|
-
/* @__PURE__ */ jsx17(LoaderCircle, { className: "animate-spin text-gray-500" }),
|
|
1194
|
-
/* @__PURE__ */ jsx17("p", { className: "text-gray-500 font-semibold", children: loadingLabel })
|
|
1195
|
-
] }) : /* @__PURE__ */ jsx17(
|
|
1196
|
-
"button",
|
|
1197
|
-
{
|
|
1198
|
-
type: "submit",
|
|
1199
|
-
className: "bg-gray-900 text-white px-6 py-2 rounded-md hover:bg-gray-700 hover:cursor-pointer transition-colors duration-300 font-semibold shadow-md",
|
|
1200
|
-
disabled: isLoading,
|
|
1201
|
-
children: label
|
|
1202
|
-
}
|
|
1203
|
-
) });
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
// packages/ui/src/pages/session-expired.tsx
|
|
1207
|
-
import { useRouter } from "next/navigation";
|
|
1208
|
-
import { useEffect as useEffect6, useState as useState8 } from "react";
|
|
1209
|
-
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1210
|
-
function SessionExpired({ redirectUrl = "/" }) {
|
|
1211
|
-
const router = useRouter();
|
|
1212
|
-
const [countdown, setCountdown] = useState8(10);
|
|
1213
|
-
useEffect6(() => {
|
|
1214
|
-
if (countdown <= 0) {
|
|
1215
|
-
router.push(redirectUrl);
|
|
1216
|
-
return;
|
|
1217
|
-
}
|
|
1218
|
-
const timer = setTimeout(() => setCountdown((c) => c - 1), 1e3);
|
|
1219
|
-
return () => clearTimeout(timer);
|
|
1220
|
-
}, [countdown, router, redirectUrl]);
|
|
1221
|
-
const handleConnect = () => {
|
|
1222
|
-
router.push(redirectUrl);
|
|
1223
|
-
};
|
|
1224
|
-
return /* @__PURE__ */ jsx18("div", { className: "min-h-screen flex items-center justify-center bg-gray-50", children: /* @__PURE__ */ jsxs14("div", { className: "max-w-md w-full text-center space-y-8 p-8", children: [
|
|
1225
|
-
/* @__PURE__ */ jsxs14("div", { className: "space-y-3", children: [
|
|
1226
|
-
/* @__PURE__ */ jsx18("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx18(
|
|
1227
|
-
"svg",
|
|
1228
|
-
{
|
|
1229
|
-
className: "w-16 h-16 text-gray-300",
|
|
1230
|
-
fill: "none",
|
|
1231
|
-
viewBox: "0 0 24 24",
|
|
1232
|
-
stroke: "currentColor",
|
|
1233
|
-
strokeWidth: 1.5,
|
|
1234
|
-
children: /* @__PURE__ */ jsx18(
|
|
1235
|
-
"path",
|
|
1236
|
-
{
|
|
1237
|
-
strokeLinecap: "round",
|
|
1238
|
-
strokeLinejoin: "round",
|
|
1239
|
-
d: "M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z"
|
|
1240
|
-
}
|
|
1241
|
-
)
|
|
1242
|
-
}
|
|
1243
|
-
) }),
|
|
1244
|
-
/* @__PURE__ */ jsx18("h1", { className: "text-2xl font-semibold text-gray-900", children: "Session expir\xE9e" }),
|
|
1245
|
-
/* @__PURE__ */ jsx18("p", { className: "text-gray-500", children: "Votre session a expir\xE9 ou vous n'\xEAtes pas connect\xE9. Veuillez vous reconnecter pour acc\xE9der \xE0 cette page." })
|
|
1246
|
-
] }),
|
|
1247
|
-
/* @__PURE__ */ jsx18(
|
|
1248
|
-
"button",
|
|
1249
|
-
{
|
|
1250
|
-
onClick: handleConnect,
|
|
1251
|
-
className: "bg-gray-900 text-white px-6 py-2.5 rounded-md text-sm font-semibold hover:bg-gray-700 transition-colors",
|
|
1252
|
-
children: "Me connecter"
|
|
1253
|
-
}
|
|
1254
|
-
),
|
|
1255
|
-
/* @__PURE__ */ jsxs14("p", { className: "text-sm text-gray-400", children: [
|
|
1256
|
-
"Redirection automatique dans ",
|
|
1257
|
-
countdown,
|
|
1258
|
-
"s..."
|
|
1259
|
-
] })
|
|
1260
|
-
] }) });
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
// packages/ui/src/pages/access-denied.tsx
|
|
1264
|
-
import Image from "next/image";
|
|
1265
|
-
import Link from "next/link";
|
|
1266
|
-
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1267
|
-
function AccessDenied({ logo, redirectTo, textRedirection }) {
|
|
1268
|
-
return /* @__PURE__ */ jsx19("main", { className: "min-h-screen py-16 px-4 bg-gradient-to-b from-gray-100 to-white text-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsxs15("div", { className: "max-w-md mx-auto text-center", children: [
|
|
1269
|
-
/* @__PURE__ */ jsx19("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ jsx19(
|
|
1270
|
-
Image,
|
|
1271
|
-
{
|
|
1272
|
-
alt: "Logo",
|
|
1273
|
-
height: 100,
|
|
1274
|
-
width: 100,
|
|
1275
|
-
src: logo
|
|
1276
|
-
}
|
|
1277
|
-
) }),
|
|
1278
|
-
/* @__PURE__ */ jsx19("h1", { className: "text-2xl font-bold text-red-600 mb-4", children: "Acc\xE8s refus\xE9" }),
|
|
1279
|
-
/* @__PURE__ */ jsx19("p", { className: "text-gray-500 mb-8", children: "Vous ne disposez pas des droits n\xE9cessaires pour acc\xE9der \xE0 cette page." }),
|
|
1280
|
-
/* @__PURE__ */ jsx19(
|
|
1281
|
-
Link,
|
|
1282
|
-
{
|
|
1283
|
-
href: redirectTo,
|
|
1284
|
-
className: "inline-block px-6 py-3 bg-gray-900 text-white rounded-lg font-semibold hover:bg-gray-700 transition-colors",
|
|
1285
|
-
children: textRedirection
|
|
1286
|
-
}
|
|
1287
|
-
)
|
|
1288
|
-
] }) });
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
// packages/ui/src/route-guard/route-guard.tsx
|
|
1292
|
-
import {
|
|
1293
|
-
createContext,
|
|
1294
|
-
useContext,
|
|
1295
|
-
useMemo as useMemo2
|
|
1296
|
-
} from "react";
|
|
1297
|
-
import { redirect, usePathname } from "next/navigation";
|
|
1298
|
-
import { Fragment as Fragment4, jsx as jsx20 } from "react/jsx-runtime";
|
|
1299
|
-
function createRouteGuardConfig(input) {
|
|
1300
|
-
return {
|
|
1301
|
-
useCurrentUser: input.useCurrentUser,
|
|
1302
|
-
pagePermissions: input.pagePermissions,
|
|
1303
|
-
deniedPath: input.deniedPath ?? "/access-denied"
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
var RouteGuardContext = createContext(null);
|
|
1307
|
-
function RouteGuardProvider({
|
|
1308
|
-
children,
|
|
1309
|
-
config,
|
|
1310
|
-
useCurrentUser,
|
|
1311
|
-
pagePermissions,
|
|
1312
|
-
deniedPath
|
|
1313
|
-
}) {
|
|
1314
|
-
const resolved = useMemo2(() => {
|
|
1315
|
-
if (config) return config;
|
|
1316
|
-
return {
|
|
1317
|
-
useCurrentUser,
|
|
1318
|
-
pagePermissions,
|
|
1319
|
-
deniedPath: deniedPath ?? "/access-denied"
|
|
1320
|
-
};
|
|
1321
|
-
}, [config, useCurrentUser, pagePermissions, deniedPath]);
|
|
1322
|
-
if (!resolved.useCurrentUser || !resolved.pagePermissions) {
|
|
1323
|
-
throw new Error(
|
|
1324
|
-
"RouteGuardProvider requires either a `config` prop (from createRouteGuardConfig) or `useCurrentUser` + `pagePermissions` directly."
|
|
1325
|
-
);
|
|
1326
|
-
}
|
|
1327
|
-
return /* @__PURE__ */ jsx20(RouteGuardContext.Provider, { value: resolved, children: /* @__PURE__ */ jsx20(AutoRouteGuard, { config: resolved, children }) });
|
|
1328
|
-
}
|
|
1329
|
-
function AutoRouteGuard({
|
|
1330
|
-
config,
|
|
1331
|
-
children
|
|
1332
|
-
}) {
|
|
1333
|
-
const pathname = usePathname();
|
|
1334
|
-
const { data: user, isLoading } = config.useCurrentUser();
|
|
1335
|
-
const allowedRoles = useMemo2(
|
|
1336
|
-
() => findAllowedRoles(pathname, config.pagePermissions),
|
|
1337
|
-
[pathname, config.pagePermissions]
|
|
1338
|
-
);
|
|
1339
|
-
if (!allowedRoles) {
|
|
1340
|
-
return /* @__PURE__ */ jsx20(Fragment4, { children });
|
|
1341
|
-
}
|
|
1342
|
-
if (isLoading) {
|
|
1343
|
-
return /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsx20("p", { className: "text-gray-500", children: "Chargement..." }) });
|
|
1344
|
-
}
|
|
1345
|
-
if (!user || !allowedRoles.includes(user.role.name)) {
|
|
1346
|
-
redirect(config.deniedPath);
|
|
1347
|
-
}
|
|
1348
|
-
return /* @__PURE__ */ jsx20(Fragment4, { children });
|
|
1349
|
-
}
|
|
1350
|
-
function routeGuard(page, Component) {
|
|
1351
|
-
return function GuardedPage(props) {
|
|
1352
|
-
const config = useContext(RouteGuardContext);
|
|
1353
|
-
if (!config) {
|
|
1354
|
-
throw new Error(
|
|
1355
|
-
"routeGuard requires a RouteGuardProvider ancestor. Wrap your layout with <RouteGuardProvider>."
|
|
1356
|
-
);
|
|
1357
|
-
}
|
|
1358
|
-
const { data: user, isLoading } = config.useCurrentUser();
|
|
1359
|
-
const allowedRoles = findAllowedRoles(page, config.pagePermissions);
|
|
1360
|
-
if (!allowedRoles) {
|
|
1361
|
-
return /* @__PURE__ */ jsx20(Component, { ...props });
|
|
1362
|
-
}
|
|
1363
|
-
if (isLoading) {
|
|
1364
|
-
return /* @__PURE__ */ jsx20("div", { className: "flex items-center justify-center min-h-screen", children: /* @__PURE__ */ jsx20("p", { className: "text-gray-500", children: "Chargement..." }) });
|
|
1365
|
-
}
|
|
1366
|
-
if (!user || !allowedRoles.includes(user.role.name)) {
|
|
1367
|
-
redirect(config.deniedPath);
|
|
1368
|
-
}
|
|
1369
|
-
return /* @__PURE__ */ jsx20(Component, { ...props });
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1372
|
-
function findAllowedRoles(page, pagePermissions) {
|
|
1373
|
-
const segments = page.split("/").filter(Boolean);
|
|
1374
|
-
for (let i = 0; i <= segments.length; i++) {
|
|
1375
|
-
const candidate = "/" + segments.slice(0, i).join("/");
|
|
1376
|
-
const roles = pagePermissions[candidate];
|
|
1377
|
-
if (roles) return roles;
|
|
1378
|
-
}
|
|
1379
|
-
return void 0;
|
|
1380
|
-
}
|
|
1381
|
-
export {
|
|
1382
|
-
AccessDenied,
|
|
1383
|
-
ActionsMenu,
|
|
1384
|
-
ApiError,
|
|
1385
|
-
Confirmation,
|
|
1386
|
-
DatePicker,
|
|
1387
|
-
FieldContainer,
|
|
1388
|
-
Form,
|
|
1389
|
-
HeaderContent,
|
|
1390
|
-
Input,
|
|
1391
|
-
Label,
|
|
1392
|
-
LoaderSubmit,
|
|
1393
|
-
Loading,
|
|
1394
|
-
Modal,
|
|
1395
|
-
Option,
|
|
1396
|
-
RouteGuardProvider,
|
|
1397
|
-
Select,
|
|
1398
|
-
SessionExpired,
|
|
1399
|
-
SideNav,
|
|
1400
|
-
Table,
|
|
1401
|
-
TextArea,
|
|
1402
|
-
createRouteGuardConfig,
|
|
1403
|
-
createSideNav,
|
|
1404
|
-
fetcher,
|
|
1405
|
-
routeGuard
|
|
1406
|
-
};
|
|
1407
|
-
//# sourceMappingURL=client.js.map
|