ptechcore_ui 1.0.27 → 1.0.30

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/index.cjs CHANGED
@@ -39,7 +39,10 @@ __export(index_exports, {
39
39
  AuthServices: () => AuthServices,
40
40
  CHOICES: () => Choices_default,
41
41
  CountrySelector: () => CountrySelector,
42
+ DataTable: () => DataTable,
42
43
  DateInput: () => DateInput,
44
+ DocumentFooter: () => DocumentFooter,
45
+ DocumentHeader: () => DocumentHeader,
43
46
  EntityFileManager: () => EntityFileManager,
44
47
  FDrawer: () => FDrawer,
45
48
  FetchApi: () => FetchApi,
@@ -47,33 +50,44 @@ __export(index_exports, {
47
50
  FileManager: () => FileManager,
48
51
  FileManagerProvider: () => FileManagerProvider,
49
52
  ForeignCurrencySelector: () => ForeignCurrencySelector,
53
+ InfoBox: () => InfoBox,
50
54
  InputField: () => InputField,
51
55
  InvoiceTypeSelector: () => InvoiceTypeSelector,
52
56
  LegalFormSelector: () => LegalFormSelector,
53
57
  Modal: () => Modals_default,
54
58
  NumberInput: () => NumberInput,
59
+ PRINT_GREEN: () => PRINT_GREEN,
55
60
  Pages: () => Pages_default,
56
61
  PaymentMethodSelector: () => PaymentMethodSelector,
57
62
  PrimaryButton: () => Buttons_default,
63
+ PrintPreview: () => PrintPreview,
64
+ PrintableDocument: () => PrintableDocument,
58
65
  RewiseLayout: () => ModernDoubleSidebarLayout_default,
59
66
  SecondaryButton: () => SecondaryButton,
60
67
  SelectCostCenter: () => SelectCostCenter,
61
68
  SelectDepartment: () => SelectDepartment,
62
69
  SelectInput: () => SelectInput,
70
+ SelectUnit: () => SelectUnit,
63
71
  SelectUser: () => SelectUser,
64
72
  SelectVendor: () => SelectVendor,
65
73
  SessionProvider: () => SessionProvider,
74
+ SignatureSection: () => SignatureSection,
66
75
  TaxSelector: () => TaxSelector,
67
76
  TemplateFNESelector: () => TemplateFNESelector,
68
77
  TextInput: () => TextInput,
69
78
  ThemeProvider: () => ThemeContext_default,
70
79
  ToastContainer: () => Toast_default,
71
80
  ToastProvider: () => ToastProvider,
81
+ TotalsSection: () => TotalsSection,
82
+ UnitServices: () => UnitServices,
72
83
  UserServices: () => UserServices,
73
84
  fileManagerApi: () => fileManagerApi,
85
+ formatCurrency: () => formatCurrency,
74
86
  formatDate: () => formatDate,
87
+ formatDateFR: () => formatDateFR,
75
88
  formatFileSize: () => formatFileSize,
76
89
  getFileIcon: () => getFileIcon,
90
+ numberToWords: () => numberToWords,
77
91
  useAlert: () => useAlert,
78
92
  useFileManager: () => useFileManager,
79
93
  useFileManagerApi: () => useFileManagerApi,
@@ -95,9 +109,9 @@ var PrimaryButton = ({
95
109
  {
96
110
  type: "submit",
97
111
  disabled: loading || props.disabled,
98
- className: `px-4 py-2 text-sm rounded-lg hover:bg-opacity-80 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex justify-center items-center ${classname} ${variant === "full" ? "bg-[#6A8A82] text-white" : variant === "outline" ? "border border-[#6A8A82] text-[#6A8A82] bg-transparent" : "bg-transparent text-[#6A8A82]"}`,
112
+ className: `px-4 py-2 text-sm rounded-lg hover:opacity-80 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex justify-center items-center ${classname} ${variant === "full" ? "bg-[var(--color-primary)] text-[var(--color-text-inverse)]" : variant === "outline" ? "border border-[var(--color-primary)] text-[var(--color-primary)] bg-transparent" : "bg-transparent text-[var(--color-primary)]"}`,
99
113
  ...props,
100
- children: loading ? "Connexion en cours..." : children
114
+ children: loading ? "Chargement..." : children
101
115
  }
102
116
  );
103
117
  var SecondaryButton = ({
@@ -110,9 +124,9 @@ var SecondaryButton = ({
110
124
  {
111
125
  type: "button",
112
126
  disabled: loading || props.disabled,
113
- className: `px-4 py-2 rounded-lg hover:bg-opacity-80 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center ${variant === "full" ? "bg-[#B87333] text-white" : variant === "outline" ? "border border-[#B87333] text-[#B87333] bg-transparent" : "bg-transparent text-[#B87333]"}`,
127
+ className: `px-4 py-2 text-sm rounded-lg hover:opacity-80 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center ${variant === "full" ? "bg-[var(--color-secondary)] text-[var(--color-text-inverse)]" : variant === "outline" ? "border border-[var(--color-secondary)] text-[var(--color-secondary)] bg-transparent" : "bg-transparent text-[var(--color-secondary)]"}`,
114
128
  ...props,
115
- children: loading ? "Connexion en cours..." : children
129
+ children: loading ? "Chargement..." : children
116
130
  }
117
131
  );
118
132
  var Buttons_default = PrimaryButton;
@@ -120,7 +134,7 @@ var Buttons_default = PrimaryButton;
120
134
  // src/components/common/Modals.tsx
121
135
  var import_react_dom = require("react-dom");
122
136
  var import_jsx_runtime2 = require("react/jsx-runtime");
123
- var Modal = ({ title, description, width = "max-w-md", open, onClose, children }) => {
137
+ var Modal = ({ title, description, width = "max-w-md", minContentHeight, open, onClose, children }) => {
124
138
  if (!open) return null;
125
139
  const modalContent = /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[9999]", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `bg-[var(--color-surface)] rounded-lg p-6 mx-4 w-full ${width}`, children: [
126
140
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex justify-between items-start mb-6", children: [
@@ -138,7 +152,14 @@ var Modal = ({ title, description, width = "max-w-md", open, onClose, children }
138
152
  }
139
153
  )
140
154
  ] }),
141
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-full max-h-[80vh] overflow-y-auto", children })
155
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
156
+ "div",
157
+ {
158
+ className: "w-full max-h-[80vh] overflow-y-auto",
159
+ style: minContentHeight ? { minHeight: minContentHeight } : void 0,
160
+ children
161
+ }
162
+ )
142
163
  ] }) });
143
164
  return (0, import_react_dom.createPortal)(modalContent, document.body);
144
165
  };
@@ -160,10 +181,10 @@ var InputField = ({
160
181
  onBlur
161
182
  }) => {
162
183
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col gap-1 w-full", children: [
163
- label && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { htmlFor: name, className: "block text-gray-700 text-sm font-medium mb-2", children: [
184
+ label && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { htmlFor: name, className: "block text-[var(--color-text-secondary)] text-sm font-medium mb-2", children: [
164
185
  label,
165
186
  " ",
166
- required && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-red-500", children: "*" })
187
+ required && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[var(--color-error)]", children: "*" })
167
188
  ] }),
168
189
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
169
190
  "input",
@@ -177,13 +198,15 @@ var InputField = ({
177
198
  disabled,
178
199
  onChange,
179
200
  onBlur,
180
- className: `w-full px-3 py-2 border border-[#D9D9D9] focus:ring-2 focus:ring-[#6A8A82]/20
181
- ${error ? "border-red-500" : "border-gray-300"}
182
- ${disabled ? "bg-gray-100 cursor-not-allowed" : ""}
201
+ className: `w-full px-3 py-2 border rounded-lg text-sm
202
+ bg-[var(--color-surface)] text-[var(--color-text-primary)]
203
+ focus:ring-2 focus:ring-[var(--color-primary)]/20 focus:border-[var(--color-primary)] focus:outline-none
204
+ ${error ? "border-[var(--color-error)]" : "border-[var(--color-border)]"}
205
+ ${disabled ? "bg-[var(--color-background)] cursor-not-allowed opacity-60" : ""}
183
206
  `
184
207
  }
185
208
  ),
186
- error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-xs text-red-500", children: error })
209
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-xs text-[var(--color-error)]", children: error })
187
210
  ] });
188
211
  };
189
212
  var TextInput = (props) => {
@@ -213,11 +236,11 @@ var SelectInput = ({
213
236
  "label",
214
237
  {
215
238
  htmlFor: name,
216
- className: "block text-gray-700 text-sm font-medium mb-2",
239
+ className: "block text-[var(--color-text-secondary)] text-sm font-medium mb-2",
217
240
  children: [
218
241
  label,
219
242
  " ",
220
- required && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-red-500", children: "*" })
243
+ required && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[var(--color-error)]", children: "*" })
221
244
  ]
222
245
  }
223
246
  ),
@@ -232,9 +255,10 @@ var SelectInput = ({
232
255
  onChange,
233
256
  onBlur,
234
257
  className: `w-full px-4 py-2 border rounded-lg text-sm
235
- focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
236
- ${error ? "border-red-500" : "border-gray-300"}
237
- ${disabled ? "bg-gray-100 cursor-not-allowed" : ""}
258
+ bg-[var(--color-surface)] text-[var(--color-text-primary)]
259
+ focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)]/20 focus:border-[var(--color-primary)]
260
+ ${error ? "border-[var(--color-error)]" : "border-[var(--color-border)]"}
261
+ ${disabled ? "bg-[var(--color-background)] cursor-not-allowed opacity-60" : ""}
238
262
  `,
239
263
  children: [
240
264
  defaultValue !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: typeof defaultValue === "string" ? defaultValue : "S\xE9lectionnez une option" }),
@@ -242,7 +266,7 @@ var SelectInput = ({
242
266
  ]
243
267
  }
244
268
  ),
245
- error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-xs text-red-500", children: error })
269
+ error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-xs text-[var(--color-error)]", children: error })
246
270
  ] });
247
271
  };
248
272
  var addressIpformMedia = "http://localhost:8000/media/";
@@ -874,6 +898,7 @@ var AuthServices = {
874
898
  addUser: (payload) => FetchApi.post(`${API_BASE_URL}add-user/`, payload),
875
899
  login: (payload) => FetchApi.post(`${API_BASE_URL}login/`, payload),
876
900
  getUserInformations: (token) => FetchApi.get(`${API_BASE_URL}user-informations/`, token),
901
+ getUserInformationsByOldId: (id) => FetchApi.get(`${API_BASE_URL}user-informations-by-old-id/${id}/`),
877
902
  logout: () => FetchApi.post(`${API_BASE_URL}logout/`)
878
903
  };
879
904
 
@@ -917,13 +942,31 @@ var SessionProvider = ({ children }) => {
917
942
  const [isLoading, setIsLoading] = (0, import_react2.useState)(true);
918
943
  const [showAuthModal, setShowAuthModal] = (0, import_react2.useState)(false);
919
944
  (0, import_react2.useEffect)(() => {
920
- const params = new URLSearchParams(window.location.search);
921
- const tkn = params.get("tkn");
922
- if (tkn) {
923
- localStorage.setItem("token", tkn);
924
- setToken(tkn);
925
- window.history.replaceState({}, document.title, window.location.pathname);
926
- }
945
+ const initializeSession = async () => {
946
+ const params = new URLSearchParams(window.location.search);
947
+ const tkn = params.get("tkn");
948
+ if (tkn) {
949
+ localStorage.setItem("token", tkn);
950
+ setToken(tkn);
951
+ window.history.replaceState({}, document.title, window.location.pathname);
952
+ }
953
+ const old_rewise_user_id = params.get("rewise_user_id");
954
+ if (old_rewise_user_id) {
955
+ try {
956
+ const res = await AuthServices.getUserInformationsByOldId(parseInt(old_rewise_user_id));
957
+ const result = res;
958
+ if (result.success === true) {
959
+ setLoggedUser(result.data.user);
960
+ setActiveBusinessEntity(
961
+ result.data.user.centers_access.find((item) => parseInt(String(item.id)) === parseInt(saved_center_id)) || result.data.user.centers_access[0] || null
962
+ );
963
+ }
964
+ } catch (error) {
965
+ console.error("Failed to refresh session:", error);
966
+ }
967
+ }
968
+ };
969
+ initializeSession();
927
970
  }, []);
928
971
  const [vendors, setVendors] = (0, import_react2.useState)(() => {
929
972
  const cacheKey = `vendors_cache_${activeBusinessEntity?.id || "default"}`;
@@ -971,6 +1014,21 @@ var SessionProvider = ({ children }) => {
971
1014
  localStorage.removeItem("token");
972
1015
  setToken(null);
973
1016
  };
1017
+ const refreshSession = async () => {
1018
+ if (!token) return;
1019
+ try {
1020
+ const res = await AuthServices.getUserInformations(token);
1021
+ const result = res;
1022
+ if (result.success === true) {
1023
+ setLoggedUser(result.data.user);
1024
+ setActiveBusinessEntity(
1025
+ result.data.user.centers_access.find((item) => parseInt(String(item.id)) === parseInt(saved_center_id)) || result.data.user.centers_access[0] || null
1026
+ );
1027
+ }
1028
+ } catch (error) {
1029
+ console.error("Failed to refresh session:", error);
1030
+ }
1031
+ };
974
1032
  (0, import_react2.useEffect)(() => {
975
1033
  if (token) {
976
1034
  AuthServices.getUserInformations(token).then((res) => {
@@ -1003,6 +1061,7 @@ var SessionProvider = ({ children }) => {
1003
1061
  logout,
1004
1062
  showAuthModal,
1005
1063
  setShowAuthModal,
1064
+ refreshSession,
1006
1065
  vendors,
1007
1066
  setVendors,
1008
1067
  loadingVendors,
@@ -1077,16 +1136,17 @@ var ToastProvider = ({ children }) => {
1077
1136
  const addToast = (0, import_react3.useCallback)((toast) => {
1078
1137
  const id = generateId();
1079
1138
  const defaultDuration = toast.type === "error" ? 7e3 : 3e3;
1139
+ const duration = toast.duration ?? defaultDuration;
1080
1140
  const newToast = {
1141
+ ...toast,
1081
1142
  id,
1082
- duration: toast.duration ?? defaultDuration,
1083
- ...toast
1143
+ duration
1084
1144
  };
1085
1145
  setToasts((prev) => [...prev, newToast]);
1086
- if (newToast.duration && newToast.duration > 0) {
1146
+ if (duration > 0) {
1087
1147
  setTimeout(() => {
1088
- removeToast(id);
1089
- }, newToast.duration);
1148
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1149
+ }, duration);
1090
1150
  }
1091
1151
  }, []);
1092
1152
  const removeToast = (0, import_react3.useCallback)((id) => {
@@ -3473,6 +3533,16 @@ var Pages = ({
3473
3533
  };
3474
3534
  var Pages_default = Pages;
3475
3535
 
3536
+ // src/services/UnitServices.ts
3537
+ var URI = `${API_URL}/crm/units/`;
3538
+ var UnitServices = {
3539
+ create: (data) => FetchApi.post(`${URI}`, data),
3540
+ get: (id) => FetchApi.get(`${URI}${id}/`),
3541
+ list: (params) => FetchApi.get(`${URI}?${new URLSearchParams(params).toString()}`),
3542
+ update: (id, data) => FetchApi.put(`${URI}${id}/`, data),
3543
+ delete: (id) => FetchApi.delete(`${URI}${id}/`)
3544
+ };
3545
+
3476
3546
  // src/components/common/FDrawer.tsx
3477
3547
  var import_react8 = __toESM(require("react"), 1);
3478
3548
  var import_react_router_dom4 = require("react-router-dom");
@@ -5354,7 +5424,7 @@ var ApprovalWorkflow = ({
5354
5424
  if (CustomBtn) {
5355
5425
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
5356
5426
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CustomBtn, { onClick: open_modal }),
5357
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Modals_default, { open: isOpen, onClose: close_modal, title, children: formulaire() })
5427
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Modals_default, { open: isOpen, onClose: close_modal, title, width: "max-w-lg", children: formulaire() })
5358
5428
  ] });
5359
5429
  }
5360
5430
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_jsx_runtime14.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
@@ -5552,7 +5622,7 @@ var AddStageButton = ({
5552
5622
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
5553
5623
  Modals_default,
5554
5624
  {
5555
- width: "",
5625
+ width: "max-w-lg",
5556
5626
  title: "Ajouter une personne",
5557
5627
  description: "S\xE9lectionnez un utilisateur interne ou ajoutez un validateur externe",
5558
5628
  open: showModal,
@@ -5871,7 +5941,7 @@ var MinimalVendorForm = ({
5871
5941
  Modals_default,
5872
5942
  {
5873
5943
  title: "Ajouter un fournisseur",
5874
- width: "w-[100%]",
5944
+ width: "max-w-2xl",
5875
5945
  description: ``,
5876
5946
  open: isOpen,
5877
5947
  onClose,
@@ -5926,17 +5996,17 @@ var MinimalVendorForm = ({
5926
5996
  };
5927
5997
 
5928
5998
  // src/services/DepartmentServices.ts
5929
- var URI = `${API_URL}/core/departments/`;
5999
+ var URI2 = `${API_URL}/core/departments/`;
5930
6000
  var DepartmentServices = {
5931
- create: (data) => FetchApi.post(`${URI}`, data),
5932
- get: (id) => FetchApi.get(`${URI}${id}/`),
5933
- list: (params) => FetchApi.get(`${URI}?${new URLSearchParams(params).toString()}`),
5934
- update: (id, data) => FetchApi.put(`${URI}${id}/`, data),
5935
- delete: (id) => FetchApi.delete(`${URI}${id}/`)
6001
+ create: (data) => FetchApi.post(`${URI2}`, data),
6002
+ get: (id) => FetchApi.get(`${URI2}${id}/`),
6003
+ list: (params) => FetchApi.get(`${URI2}?${new URLSearchParams(params).toString()}`),
6004
+ update: (id, data) => FetchApi.put(`${URI2}${id}/`, data),
6005
+ delete: (id) => FetchApi.delete(`${URI2}${id}/`)
5936
6006
  };
5937
6007
 
5938
6008
  // src/services/ProfitCostsServices.ts
5939
- var URI2 = `${API_URL}/accounting/profit-or-cost-center/`;
6009
+ var URI3 = `${API_URL}/accounting/profit-or-cost-center/`;
5940
6010
  var COST_URI = `${API_URL}/accounting/cost-center/`;
5941
6011
  var CostServices = {
5942
6012
  create: (data) => FetchApi.post(`${COST_URI}`, data),
@@ -6243,6 +6313,80 @@ var SelectCostCenter = ({
6243
6313
  loading && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm text-gray-500 mt-2", children: "Chargement des centres de co\xFBt..." })
6244
6314
  ] });
6245
6315
  };
6316
+ var SelectUnit = ({
6317
+ value,
6318
+ onSelect
6319
+ }) => {
6320
+ const { token, activeBusinessEntity } = useSession();
6321
+ const [units, setUnits] = (0, import_react13.useState)(() => {
6322
+ const cacheKey = `units_cache_${activeBusinessEntity?.id || "default"}`;
6323
+ const cached = sessionStorage.getItem(cacheKey);
6324
+ return cached ? JSON.parse(cached) : [];
6325
+ });
6326
+ const [loading, setLoading] = (0, import_react13.useState)(false);
6327
+ (0, import_react13.useEffect)(() => {
6328
+ const cacheKey = `units_cache_${activeBusinessEntity?.id || "default"}`;
6329
+ const cached = sessionStorage.getItem(cacheKey);
6330
+ if (!cached) {
6331
+ loadUnits();
6332
+ } else {
6333
+ setUnits(JSON.parse(cached));
6334
+ }
6335
+ }, [activeBusinessEntity?.id]);
6336
+ const loadUnits = async () => {
6337
+ if (!token) return;
6338
+ try {
6339
+ setLoading(true);
6340
+ const result = await UnitServices.list({ business_entity_id: activeBusinessEntity?.id });
6341
+ if (result.success) {
6342
+ setUnits(result.data);
6343
+ const cacheKey = `units_cache_${activeBusinessEntity?.id || "default"}`;
6344
+ sessionStorage.setItem(cacheKey, JSON.stringify(result.data));
6345
+ }
6346
+ } catch (error) {
6347
+ console.error(error);
6348
+ } finally {
6349
+ setLoading(false);
6350
+ }
6351
+ };
6352
+ const handleRefresh = () => {
6353
+ const cacheKey = `units_cache_${activeBusinessEntity?.id || "default"}`;
6354
+ sessionStorage.removeItem(cacheKey);
6355
+ loadUnits();
6356
+ };
6357
+ const getUnitOptions = () => {
6358
+ return units.map((unit) => ({
6359
+ value: unit.id,
6360
+ label: `${unit.code ? `[${unit.code}] ` : ""}${unit.name || "Sans nom"}`,
6361
+ object: unit,
6362
+ content: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex items-center space-x-3", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex-1", children: [
6363
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "font-medium text-gray-900", children: unit.name || "Sans nom" }),
6364
+ unit.code && /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "text-sm text-gray-500", children: [
6365
+ "Code: ",
6366
+ unit.code
6367
+ ] }),
6368
+ unit.location && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "text-xs text-gray-400", children: unit.location })
6369
+ ] }) })
6370
+ }));
6371
+ };
6372
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
6373
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "flex justify-between ", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "S\xE9lectionner une unit\xE9" }) }),
6374
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
6375
+ SearchableSelect,
6376
+ {
6377
+ value,
6378
+ options: getUnitOptions(),
6379
+ placeholder: "S\xE9lectionner une unit\xE9 ...",
6380
+ searchPlaceholder: "Rechercher...",
6381
+ onSelect,
6382
+ disabled: loading,
6383
+ refresh: handleRefresh
6384
+ },
6385
+ "unit" + value
6386
+ ),
6387
+ loading && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm text-gray-500 mt-2", children: "Chargement des unit\xE9s..." })
6388
+ ] });
6389
+ };
6246
6390
 
6247
6391
  // src/components/common/Choices.tsx
6248
6392
  var import_jsx_runtime19 = require("react/jsx-runtime");
@@ -8254,6 +8398,384 @@ var EntityFileManager = ({
8254
8398
  }
8255
8399
  ) });
8256
8400
  };
8401
+
8402
+ // src/components/common/PrintPreview.tsx
8403
+ var import_react23 = __toESM(require("react"), 1);
8404
+ var import_react_to_print = require("react-to-print");
8405
+ var import_lucide_react18 = require("lucide-react");
8406
+ var import_jsx_runtime30 = require("react/jsx-runtime");
8407
+ var PrintableDocument = ({
8408
+ children,
8409
+ className = "",
8410
+ style = {}
8411
+ }) => {
8412
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8413
+ "div",
8414
+ {
8415
+ className: `bg-white text-gray-900 ${className}`,
8416
+ style: {
8417
+ fontFamily: "Arial, sans-serif",
8418
+ fontSize: "11px",
8419
+ ...style
8420
+ },
8421
+ children
8422
+ }
8423
+ );
8424
+ };
8425
+ var PrintPreview = ({
8426
+ children,
8427
+ isOpen,
8428
+ onClose,
8429
+ title = "Aper\xE7u avant impression",
8430
+ documentName = "document",
8431
+ pageWidth = "210mm",
8432
+ pageMinHeight = "297mm",
8433
+ orientation = "portrait",
8434
+ onAfterPrint,
8435
+ onBeforePrint
8436
+ }) => {
8437
+ const printRef = (0, import_react23.useRef)(null);
8438
+ const [zoom, setZoom] = import_react23.default.useState(100);
8439
+ const handlePrint = (0, import_react_to_print.useReactToPrint)({
8440
+ contentRef: printRef,
8441
+ documentTitle: documentName,
8442
+ onAfterPrint: () => {
8443
+ onAfterPrint?.();
8444
+ },
8445
+ onBeforePrint
8446
+ });
8447
+ const handleZoomIn = () => {
8448
+ setZoom((prev) => Math.min(prev + 10, 150));
8449
+ };
8450
+ const handleZoomOut = () => {
8451
+ setZoom((prev) => Math.max(prev - 10, 50));
8452
+ };
8453
+ if (!isOpen) return null;
8454
+ const pageStyles = {
8455
+ width: orientation === "portrait" ? pageWidth : pageMinHeight,
8456
+ minHeight: orientation === "portrait" ? pageMinHeight : pageWidth,
8457
+ transform: `scale(${zoom / 100})`,
8458
+ transformOrigin: "top center",
8459
+ transition: "transform 0.2s ease"
8460
+ };
8461
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "fixed inset-0 z-50 overflow-hidden print:hidden", children: [
8462
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8463
+ "div",
8464
+ {
8465
+ className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
8466
+ onClick: onClose
8467
+ }
8468
+ ),
8469
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "relative h-full flex flex-col", children: [
8470
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "bg-white border-b shadow-sm px-6 py-3 flex items-center justify-between z-10", children: [
8471
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("h3", { className: "text-lg font-semibold text-gray-900", children: title }),
8472
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-3", children: [
8473
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex items-center gap-1 bg-gray-100 rounded-lg px-2 py-1", children: [
8474
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8475
+ "button",
8476
+ {
8477
+ onClick: handleZoomOut,
8478
+ className: "p-1 hover:bg-gray-200 rounded transition-colors",
8479
+ title: "Zoom arri\xE8re",
8480
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react18.ZoomOut, { className: "w-4 h-4 text-gray-600" })
8481
+ }
8482
+ ),
8483
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "text-sm text-gray-600 min-w-[3rem] text-center", children: [
8484
+ zoom,
8485
+ "%"
8486
+ ] }),
8487
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8488
+ "button",
8489
+ {
8490
+ onClick: handleZoomIn,
8491
+ className: "p-1 hover:bg-gray-200 rounded transition-colors",
8492
+ title: "Zoom avant",
8493
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react18.ZoomIn, { className: "w-4 h-4 text-gray-600" })
8494
+ }
8495
+ )
8496
+ ] }),
8497
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
8498
+ "button",
8499
+ {
8500
+ onClick: () => handlePrint(),
8501
+ className: "px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2",
8502
+ children: [
8503
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react18.Printer, { className: "w-4 h-4" }),
8504
+ "Imprimer"
8505
+ ]
8506
+ }
8507
+ ),
8508
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
8509
+ "button",
8510
+ {
8511
+ onClick: () => handlePrint(),
8512
+ className: "px-4 py-2 bg-green-600 text-white text-sm font-medium rounded-lg hover:bg-green-700 transition-colors flex items-center gap-2",
8513
+ children: [
8514
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react18.Download, { className: "w-4 h-4" }),
8515
+ "PDF"
8516
+ ]
8517
+ }
8518
+ ),
8519
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8520
+ "button",
8521
+ {
8522
+ onClick: onClose,
8523
+ className: "p-2 hover:bg-gray-100 rounded-full transition-colors",
8524
+ title: "Fermer",
8525
+ children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_lucide_react18.X, { className: "w-5 h-5 text-gray-500" })
8526
+ }
8527
+ )
8528
+ ] })
8529
+ ] }),
8530
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex-1 overflow-auto bg-gray-200 p-6", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8531
+ "div",
8532
+ {
8533
+ ref: printRef,
8534
+ className: "bg-white shadow-xl",
8535
+ style: pageStyles,
8536
+ children
8537
+ }
8538
+ ) }) })
8539
+ ] })
8540
+ ] });
8541
+ };
8542
+ var PRINT_GREEN = "#2d7d46";
8543
+ var DocumentHeader = ({
8544
+ companyName,
8545
+ address,
8546
+ phone,
8547
+ email,
8548
+ website,
8549
+ documentTitle,
8550
+ documentNumber,
8551
+ date,
8552
+ extraInfo = []
8553
+ }) => {
8554
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex justify-between items-start mb-6 p-8 pb-0", children: [
8555
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "flex-1", children: [
8556
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "mb-2", children: [
8557
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8558
+ "h1",
8559
+ {
8560
+ className: "text-xl font-bold tracking-wider text-gray-800",
8561
+ style: { letterSpacing: "0.15em" },
8562
+ children: companyName.toUpperCase()
8563
+ }
8564
+ ),
8565
+ address && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-xs text-gray-600 mt-1", children: address })
8566
+ ] }),
8567
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "text-xs text-gray-600 space-y-0.5 mt-3", children: [
8568
+ phone && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: phone }),
8569
+ email && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: email }),
8570
+ website && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: website })
8571
+ ] })
8572
+ ] }),
8573
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "text-right", children: [
8574
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8575
+ "h2",
8576
+ {
8577
+ className: "text-4xl font-bold mb-4",
8578
+ style: { color: PRINT_GREEN },
8579
+ children: documentTitle
8580
+ }
8581
+ ),
8582
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("table", { className: "ml-auto text-xs", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tbody", { children: [
8583
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tr", { children: [
8584
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "text-gray-600 pr-4 py-0.5", children: "Date" }),
8585
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "font-medium border-b border-gray-300 pl-2 py-0.5", children: date })
8586
+ ] }),
8587
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tr", { children: [
8588
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "text-gray-600 pr-4 py-0.5", children: "N\xB0" }),
8589
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "font-medium border-b border-gray-300 pl-2 py-0.5", children: documentNumber })
8590
+ ] }),
8591
+ extraInfo.map((info, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tr", { children: [
8592
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "text-gray-600 pr-4 py-0.5", children: info.label }),
8593
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("td", { className: "font-medium border-b border-gray-300 pl-2 py-0.5", children: info.value })
8594
+ ] }, index))
8595
+ ] }) })
8596
+ ] })
8597
+ ] });
8598
+ };
8599
+ var InfoBox = ({
8600
+ title,
8601
+ children,
8602
+ variant = "green"
8603
+ }) => {
8604
+ const headerBg = variant === "green" ? PRINT_GREEN : "#6b7280";
8605
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
8606
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8607
+ "div",
8608
+ {
8609
+ className: "text-white text-xs font-semibold py-1.5 px-3 rounded-t",
8610
+ style: { backgroundColor: headerBg },
8611
+ children: title
8612
+ }
8613
+ ),
8614
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "border border-gray-300 border-t-0 p-3 min-h-[80px]", children })
8615
+ ] });
8616
+ };
8617
+ function DataTable({
8618
+ columns,
8619
+ data,
8620
+ minEmptyRows = 0,
8621
+ keyExtractor
8622
+ }) {
8623
+ const emptyRowsCount = Math.max(0, minEmptyRows - data.length);
8624
+ const getNestedValue = (obj, path) => {
8625
+ return path.split(".").reduce((acc, part) => acc && acc[part], obj);
8626
+ };
8627
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("table", { className: "w-full border-collapse", children: [
8628
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tr", { style: { backgroundColor: PRINT_GREEN }, children: columns.map((col, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8629
+ "th",
8630
+ {
8631
+ className: "text-white text-xs font-semibold py-2 px-2 border border-gray-400",
8632
+ style: {
8633
+ width: col.width,
8634
+ textAlign: col.align || "left"
8635
+ },
8636
+ children: col.header
8637
+ },
8638
+ index
8639
+ )) }) }),
8640
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("tbody", { children: [
8641
+ data.map((item, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tr", { className: "border-b border-gray-300", children: columns.map((col, colIndex) => {
8642
+ const value = typeof col.key === "string" ? getNestedValue(item, col.key) : item[col.key];
8643
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8644
+ "td",
8645
+ {
8646
+ className: "py-2 px-2 text-xs text-gray-900 border-l border-r border-gray-300",
8647
+ style: { textAlign: col.align || "left" },
8648
+ children: col.render ? col.render(value, item, rowIndex) : value
8649
+ },
8650
+ colIndex
8651
+ );
8652
+ }) }, keyExtractor(item, rowIndex))),
8653
+ Array.from({ length: emptyRowsCount }).map((_, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tr", { className: "border-b border-gray-300", children: columns.map((_2, colIndex) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8654
+ "td",
8655
+ {
8656
+ className: "py-2 px-2 border-l border-r border-gray-300",
8657
+ children: "\xA0"
8658
+ },
8659
+ colIndex
8660
+ )) }, `empty-${index}`))
8661
+ ] })
8662
+ ] });
8663
+ }
8664
+ var TotalsSection = ({
8665
+ rows,
8666
+ amountInWords
8667
+ }) => {
8668
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "w-64", children: [
8669
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("table", { className: "w-full text-xs", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("tbody", { children: rows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
8670
+ "tr",
8671
+ {
8672
+ className: row.isTotal ? "" : "border-b border-gray-200",
8673
+ children: [
8674
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8675
+ "td",
8676
+ {
8677
+ className: `py-1.5 px-2 ${row.isTotal ? "font-semibold text-gray-900" : "text-gray-600"}`,
8678
+ children: row.label
8679
+ }
8680
+ ),
8681
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
8682
+ "td",
8683
+ {
8684
+ className: `py-1.5 px-2 text-right font-medium`,
8685
+ style: row.isTotal ? {
8686
+ backgroundColor: PRINT_GREEN,
8687
+ color: "white",
8688
+ fontWeight: "bold"
8689
+ } : { color: row.valueColor },
8690
+ children: row.value
8691
+ }
8692
+ )
8693
+ ]
8694
+ },
8695
+ index
8696
+ )) }) }),
8697
+ amountInWords && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "mt-2 text-xs text-gray-600 italic text-center px-2", children: amountInWords })
8698
+ ] });
8699
+ };
8700
+ var SignatureSection = ({
8701
+ date,
8702
+ leftLabel = "Signature Client",
8703
+ rightLabel = "Cachet et signature",
8704
+ rightName
8705
+ }) => {
8706
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "grid grid-cols-2 gap-8 mt-12 pt-4 px-8", children: [
8707
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { children: [
8708
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-xs text-gray-600 mb-1", children: "Date:" }),
8709
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "border-b border-gray-400 w-32 mb-4 text-xs", children: date || "" }),
8710
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("p", { className: "text-xs text-gray-600 mb-1", children: [
8711
+ leftLabel,
8712
+ ":"
8713
+ ] }),
8714
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "h-16 border-b border-gray-400 w-48" })
8715
+ ] }),
8716
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "text-right", children: [
8717
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-xs text-gray-600 mb-2", children: rightLabel }),
8718
+ rightName && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "text-xs font-medium text-gray-900 mb-2", children: rightName }),
8719
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "inline-block border-2 border-dashed border-gray-300 rounded-full w-24 h-24" })
8720
+ ] })
8721
+ ] });
8722
+ };
8723
+ var DocumentFooter = ({ lines }) => {
8724
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "mt-8 pt-3 border-t border-gray-300 text-center text-xs text-gray-500 px-8 pb-8", children: lines.map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { children: line }, index)) });
8725
+ };
8726
+ var numberToWords = (num) => {
8727
+ if (num === 0) return "z\xE9ro";
8728
+ const units = ["", "un", "deux", "trois", "quatre", "cinq", "six", "sept", "huit", "neuf", "dix", "onze", "douze", "treize", "quatorze", "quinze", "seize", "dix-sept", "dix-huit", "dix-neuf"];
8729
+ const tens = ["", "", "vingt", "trente", "quarante", "cinquante", "soixante", "soixante", "quatre-vingt", "quatre-vingt"];
8730
+ const convertHundreds = (n) => {
8731
+ if (n < 20) return units[n];
8732
+ if (n < 100) {
8733
+ const ten = Math.floor(n / 10);
8734
+ const unit = n % 10;
8735
+ if (ten === 7 || ten === 9) {
8736
+ return tens[ten] + "-" + units[10 + unit];
8737
+ }
8738
+ return tens[ten] + (unit ? "-" + units[unit] : ten === 8 ? "s" : "");
8739
+ }
8740
+ const hundred = Math.floor(n / 100);
8741
+ const rest = n % 100;
8742
+ return (hundred === 1 ? "cent" : units[hundred] + " cent") + (rest ? " " + convertHundreds(rest) : hundred > 1 && rest === 0 ? "s" : "");
8743
+ };
8744
+ const convertThousands = (n) => {
8745
+ if (n < 1e3) return convertHundreds(n);
8746
+ const thousands = Math.floor(n / 1e3);
8747
+ const rest = n % 1e3;
8748
+ return (thousands === 1 ? "mille" : convertHundreds(thousands) + " mille") + (rest ? " " + convertHundreds(rest) : "");
8749
+ };
8750
+ const convertMillions = (n) => {
8751
+ if (n < 1e6) return convertThousands(n);
8752
+ const millions = Math.floor(n / 1e6);
8753
+ const rest = n % 1e6;
8754
+ return convertHundreds(millions) + " million" + (millions > 1 ? "s" : "") + (rest ? " " + convertThousands(rest) : "");
8755
+ };
8756
+ return convertMillions(Math.floor(num));
8757
+ };
8758
+ var formatDateFR = (date, format = "short") => {
8759
+ const d = typeof date === "string" ? new Date(date) : date;
8760
+ if (format === "short") {
8761
+ return d.toLocaleDateString("fr-FR", {
8762
+ day: "2-digit",
8763
+ month: "2-digit",
8764
+ year: "numeric"
8765
+ });
8766
+ }
8767
+ return d.toLocaleDateString("fr-FR", {
8768
+ day: "numeric",
8769
+ month: "long",
8770
+ year: "numeric"
8771
+ });
8772
+ };
8773
+ var formatCurrency = (amount, currency = "XOF", showDecimals = false) => {
8774
+ return amount.toLocaleString("fr-FR", {
8775
+ minimumFractionDigits: showDecimals ? 2 : 0,
8776
+ maximumFractionDigits: showDecimals ? 2 : 0
8777
+ }) + (currency ? ` ${currency}` : "");
8778
+ };
8257
8779
  // Annotate the CommonJS export names for ESM import in node:
8258
8780
  0 && (module.exports = {
8259
8781
  Alert,
@@ -8266,7 +8788,10 @@ var EntityFileManager = ({
8266
8788
  AuthServices,
8267
8789
  CHOICES,
8268
8790
  CountrySelector,
8791
+ DataTable,
8269
8792
  DateInput,
8793
+ DocumentFooter,
8794
+ DocumentHeader,
8270
8795
  EntityFileManager,
8271
8796
  FDrawer,
8272
8797
  FetchApi,
@@ -8274,33 +8799,44 @@ var EntityFileManager = ({
8274
8799
  FileManager,
8275
8800
  FileManagerProvider,
8276
8801
  ForeignCurrencySelector,
8802
+ InfoBox,
8277
8803
  InputField,
8278
8804
  InvoiceTypeSelector,
8279
8805
  LegalFormSelector,
8280
8806
  Modal,
8281
8807
  NumberInput,
8808
+ PRINT_GREEN,
8282
8809
  Pages,
8283
8810
  PaymentMethodSelector,
8284
8811
  PrimaryButton,
8812
+ PrintPreview,
8813
+ PrintableDocument,
8285
8814
  RewiseLayout,
8286
8815
  SecondaryButton,
8287
8816
  SelectCostCenter,
8288
8817
  SelectDepartment,
8289
8818
  SelectInput,
8819
+ SelectUnit,
8290
8820
  SelectUser,
8291
8821
  SelectVendor,
8292
8822
  SessionProvider,
8823
+ SignatureSection,
8293
8824
  TaxSelector,
8294
8825
  TemplateFNESelector,
8295
8826
  TextInput,
8296
8827
  ThemeProvider,
8297
8828
  ToastContainer,
8298
8829
  ToastProvider,
8830
+ TotalsSection,
8831
+ UnitServices,
8299
8832
  UserServices,
8300
8833
  fileManagerApi,
8834
+ formatCurrency,
8301
8835
  formatDate,
8836
+ formatDateFR,
8302
8837
  formatFileSize,
8303
8838
  getFileIcon,
8839
+ numberToWords,
8304
8840
  useAlert,
8305
8841
  useFileManager,
8306
8842
  useFileManagerApi,