ptechcore_ui 1.0.25 → 1.0.29

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.js CHANGED
@@ -11,9 +11,9 @@ var PrimaryButton = ({
11
11
  {
12
12
  type: "submit",
13
13
  disabled: loading || props.disabled,
14
- 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]"}`,
14
+ 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)]"}`,
15
15
  ...props,
16
- children: loading ? "Connexion en cours..." : children
16
+ children: loading ? "Chargement..." : children
17
17
  }
18
18
  );
19
19
  var SecondaryButton = ({
@@ -26,9 +26,9 @@ var SecondaryButton = ({
26
26
  {
27
27
  type: "button",
28
28
  disabled: loading || props.disabled,
29
- 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]"}`,
29
+ 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)]"}`,
30
30
  ...props,
31
- children: loading ? "Connexion en cours..." : children
31
+ children: loading ? "Chargement..." : children
32
32
  }
33
33
  );
34
34
  var Buttons_default = PrimaryButton;
@@ -36,7 +36,7 @@ var Buttons_default = PrimaryButton;
36
36
  // src/components/common/Modals.tsx
37
37
  import { createPortal } from "react-dom";
38
38
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
39
- var Modal = ({ title, description, width = "max-w-md", open, onClose, children }) => {
39
+ var Modal = ({ title, description, width = "max-w-md", minContentHeight, open, onClose, children }) => {
40
40
  if (!open) return null;
41
41
  const modalContent = /* @__PURE__ */ jsx2("div", { className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[9999]", children: /* @__PURE__ */ jsxs("div", { className: `bg-[var(--color-surface)] rounded-lg p-6 mx-4 w-full ${width}`, children: [
42
42
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-start mb-6", children: [
@@ -54,7 +54,14 @@ var Modal = ({ title, description, width = "max-w-md", open, onClose, children }
54
54
  }
55
55
  )
56
56
  ] }),
57
- /* @__PURE__ */ jsx2("div", { className: "w-full max-h-[80vh] overflow-y-auto", children })
57
+ /* @__PURE__ */ jsx2(
58
+ "div",
59
+ {
60
+ className: "w-full max-h-[80vh] overflow-y-auto",
61
+ style: minContentHeight ? { minHeight: minContentHeight } : void 0,
62
+ children
63
+ }
64
+ )
58
65
  ] }) });
59
66
  return createPortal(modalContent, document.body);
60
67
  };
@@ -76,10 +83,10 @@ var InputField = ({
76
83
  onBlur
77
84
  }) => {
78
85
  return /* @__PURE__ */ jsxs2("div", { className: "flex flex-col gap-1 w-full", children: [
79
- label && /* @__PURE__ */ jsxs2("label", { htmlFor: name, className: "block text-gray-700 text-sm font-medium mb-2", children: [
86
+ label && /* @__PURE__ */ jsxs2("label", { htmlFor: name, className: "block text-[var(--color-text-secondary)] text-sm font-medium mb-2", children: [
80
87
  label,
81
88
  " ",
82
- required && /* @__PURE__ */ jsx3("span", { className: "text-red-500", children: "*" })
89
+ required && /* @__PURE__ */ jsx3("span", { className: "text-[var(--color-error)]", children: "*" })
83
90
  ] }),
84
91
  /* @__PURE__ */ jsx3(
85
92
  "input",
@@ -93,13 +100,15 @@ var InputField = ({
93
100
  disabled,
94
101
  onChange,
95
102
  onBlur,
96
- className: `w-full px-3 py-2 border border-[#D9D9D9] focus:ring-2 focus:ring-[#6A8A82]/20
97
- ${error ? "border-red-500" : "border-gray-300"}
98
- ${disabled ? "bg-gray-100 cursor-not-allowed" : ""}
103
+ className: `w-full px-3 py-2 border rounded-lg text-sm
104
+ bg-[var(--color-surface)] text-[var(--color-text-primary)]
105
+ focus:ring-2 focus:ring-[var(--color-primary)]/20 focus:border-[var(--color-primary)] focus:outline-none
106
+ ${error ? "border-[var(--color-error)]" : "border-[var(--color-border)]"}
107
+ ${disabled ? "bg-[var(--color-background)] cursor-not-allowed opacity-60" : ""}
99
108
  `
100
109
  }
101
110
  ),
102
- error && /* @__PURE__ */ jsx3("p", { className: "text-xs text-red-500", children: error })
111
+ error && /* @__PURE__ */ jsx3("p", { className: "text-xs text-[var(--color-error)]", children: error })
103
112
  ] });
104
113
  };
105
114
  var TextInput = (props) => {
@@ -129,11 +138,11 @@ var SelectInput = ({
129
138
  "label",
130
139
  {
131
140
  htmlFor: name,
132
- className: "block text-gray-700 text-sm font-medium mb-2",
141
+ className: "block text-[var(--color-text-secondary)] text-sm font-medium mb-2",
133
142
  children: [
134
143
  label,
135
144
  " ",
136
- required && /* @__PURE__ */ jsx3("span", { className: "text-red-500", children: "*" })
145
+ required && /* @__PURE__ */ jsx3("span", { className: "text-[var(--color-error)]", children: "*" })
137
146
  ]
138
147
  }
139
148
  ),
@@ -148,9 +157,10 @@ var SelectInput = ({
148
157
  onChange,
149
158
  onBlur,
150
159
  className: `w-full px-4 py-2 border rounded-lg text-sm
151
- focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent
152
- ${error ? "border-red-500" : "border-gray-300"}
153
- ${disabled ? "bg-gray-100 cursor-not-allowed" : ""}
160
+ bg-[var(--color-surface)] text-[var(--color-text-primary)]
161
+ focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)]/20 focus:border-[var(--color-primary)]
162
+ ${error ? "border-[var(--color-error)]" : "border-[var(--color-border)]"}
163
+ ${disabled ? "bg-[var(--color-background)] cursor-not-allowed opacity-60" : ""}
154
164
  `,
155
165
  children: [
156
166
  defaultValue !== void 0 && /* @__PURE__ */ jsx3("option", { value: "", children: typeof defaultValue === "string" ? defaultValue : "S\xE9lectionnez une option" }),
@@ -158,7 +168,7 @@ var SelectInput = ({
158
168
  ]
159
169
  }
160
170
  ),
161
- error && /* @__PURE__ */ jsx3("p", { className: "text-xs text-red-500", children: error })
171
+ error && /* @__PURE__ */ jsx3("p", { className: "text-xs text-[var(--color-error)]", children: error })
162
172
  ] });
163
173
  };
164
174
  var addressIpformMedia = "http://localhost:8000/media/";
@@ -712,7 +722,7 @@ import { createContext as createContext2, useContext as useContext2, useEffect a
712
722
 
713
723
  // src/services/api.ts
714
724
  var chooseEnv = localStorage.getItem("env") ?? "prod";
715
- var ADDRESS_IP = chooseEnv === "prod" ? "backend-core.rewise.praedium-tech.com" : "localhost:8000";
725
+ var ADDRESS_IP = chooseEnv === "prod" ? "core.backend-rewise.praedium-tech.com" : "localhost:8000";
716
726
  var ADDRESS_IP_URL = chooseEnv === "prod" ? `https://${ADDRESS_IP}/` : `http://${ADDRESS_IP}/`;
717
727
  var API_URL = `${ADDRESS_IP_URL}api`;
718
728
  var FetchApi = class {
@@ -915,6 +925,21 @@ var SessionProvider = ({ children }) => {
915
925
  localStorage.removeItem("token");
916
926
  setToken(null);
917
927
  };
928
+ const refreshSession = async () => {
929
+ if (!token) return;
930
+ try {
931
+ const res = await AuthServices.getUserInformations(token);
932
+ const result = res;
933
+ if (result.success === true) {
934
+ setLoggedUser(result.data.user);
935
+ setActiveBusinessEntity(
936
+ result.data.user.centers_access.find((item) => parseInt(String(item.id)) === parseInt(saved_center_id)) || result.data.user.centers_access[0] || null
937
+ );
938
+ }
939
+ } catch (error) {
940
+ console.error("Failed to refresh session:", error);
941
+ }
942
+ };
918
943
  useEffect2(() => {
919
944
  if (token) {
920
945
  AuthServices.getUserInformations(token).then((res) => {
@@ -942,10 +967,12 @@ var SessionProvider = ({ children }) => {
942
967
  activeBusinessEntity,
943
968
  setActiveBusinessEntity,
944
969
  token,
970
+ setToken,
945
971
  login,
946
972
  logout,
947
973
  showAuthModal,
948
974
  setShowAuthModal,
975
+ refreshSession,
949
976
  vendors,
950
977
  setVendors,
951
978
  loadingVendors,
@@ -974,9 +1001,8 @@ var UserServices = {
974
1001
  deleteUser: (id, token) => FetchApi.delete(`${USERS_API_URL}${id}/`, token),
975
1002
  // Obtenir les utilisateurs d'une entité
976
1003
  getEntityUsers: (entityId, token) => FetchApi.get(`${API_URL}/core/entities/${entityId}/users/`, token),
977
- // Obtenir les utilisateurs d'une entité
978
- getuserEntitiesAccess: (id, token) => FetchApi.get(`${API_URL}/core/entities/`, token),
979
- // !!! ce n'est pas la bonne url
1004
+ // Obtenir les entités auxquelles un utilisateur a accès
1005
+ getuserEntitiesAccess: (id, token) => FetchApi.get(`${API_URL}/core/users/${id}/entities/`, token),
980
1006
  // Ajouter un utilisateur à une entité
981
1007
  addUserToEntity: (entityId, userId, token) => FetchApi.post(`${API_URL}/core/entities/${entityId}/users/`, { user_id: userId }, token),
982
1008
  // === Nouvelles méthodes pour le profil utilisateur ===
@@ -1021,16 +1047,17 @@ var ToastProvider = ({ children }) => {
1021
1047
  const addToast = useCallback((toast) => {
1022
1048
  const id = generateId();
1023
1049
  const defaultDuration = toast.type === "error" ? 7e3 : 3e3;
1050
+ const duration = toast.duration ?? defaultDuration;
1024
1051
  const newToast = {
1052
+ ...toast,
1025
1053
  id,
1026
- duration: toast.duration ?? defaultDuration,
1027
- ...toast
1054
+ duration
1028
1055
  };
1029
1056
  setToasts((prev) => [...prev, newToast]);
1030
- if (newToast.duration && newToast.duration > 0) {
1057
+ if (duration > 0) {
1031
1058
  setTimeout(() => {
1032
- removeToast(id);
1033
- }, newToast.duration);
1059
+ setToasts((prev) => prev.filter((t) => t.id !== id));
1060
+ }, duration);
1034
1061
  }
1035
1062
  }, []);
1036
1063
  const removeToast = useCallback((id) => {
@@ -1843,7 +1870,7 @@ var RewiseLayout = ({ children, module_name = "Rewise", module_description = "De
1843
1870
  "organizations": "organizations",
1844
1871
  "entities": "entities"
1845
1872
  };
1846
- setSelectedModule(routeMapping[moduleId] || "dashboard");
1873
+ setSelectedModule(routeMapping[moduleId] || moduleId);
1847
1874
  }
1848
1875
  }, [location]);
1849
1876
  const handleThemeChange = (type) => {
@@ -3417,6 +3444,16 @@ var Pages = ({
3417
3444
  };
3418
3445
  var Pages_default = Pages;
3419
3446
 
3447
+ // src/services/UnitServices.ts
3448
+ var URI = `${API_URL}/crm/units/`;
3449
+ var UnitServices = {
3450
+ create: (data) => FetchApi.post(`${URI}`, data),
3451
+ get: (id) => FetchApi.get(`${URI}${id}/`),
3452
+ list: (params) => FetchApi.get(`${URI}?${new URLSearchParams(params).toString()}`),
3453
+ update: (id, data) => FetchApi.put(`${URI}${id}/`, data),
3454
+ delete: (id) => FetchApi.delete(`${URI}${id}/`)
3455
+ };
3456
+
3420
3457
  // src/components/common/FDrawer.tsx
3421
3458
  import React7, { useEffect as useEffect6, useState as useState8, useRef } from "react";
3422
3459
  import { useLocation as useLocation2, useNavigate as useNavigate2, useSearchParams as useSearchParams2, Link as Link2 } from "react-router-dom";
@@ -5298,7 +5335,7 @@ var ApprovalWorkflow = ({
5298
5335
  if (CustomBtn) {
5299
5336
  return /* @__PURE__ */ jsxs11(Fragment6, { children: [
5300
5337
  /* @__PURE__ */ jsx14(CustomBtn, { onClick: open_modal }),
5301
- /* @__PURE__ */ jsx14(Modals_default, { open: isOpen, onClose: close_modal, title, children: formulaire() })
5338
+ /* @__PURE__ */ jsx14(Modals_default, { open: isOpen, onClose: close_modal, title, width: "max-w-lg", children: formulaire() })
5302
5339
  ] });
5303
5340
  }
5304
5341
  return /* @__PURE__ */ jsx14(Fragment6, { children: /* @__PURE__ */ jsx14(
@@ -5496,7 +5533,7 @@ var AddStageButton = ({
5496
5533
  /* @__PURE__ */ jsx14(
5497
5534
  Modals_default,
5498
5535
  {
5499
- width: "",
5536
+ width: "max-w-lg",
5500
5537
  title: "Ajouter une personne",
5501
5538
  description: "S\xE9lectionnez un utilisateur interne ou ajoutez un validateur externe",
5502
5539
  open: showModal,
@@ -5815,7 +5852,7 @@ var MinimalVendorForm = ({
5815
5852
  Modals_default,
5816
5853
  {
5817
5854
  title: "Ajouter un fournisseur",
5818
- width: "w-[100%]",
5855
+ width: "max-w-2xl",
5819
5856
  description: ``,
5820
5857
  open: isOpen,
5821
5858
  onClose,
@@ -5870,17 +5907,17 @@ var MinimalVendorForm = ({
5870
5907
  };
5871
5908
 
5872
5909
  // src/services/DepartmentServices.ts
5873
- var URI = `${API_URL}/core/departments/`;
5910
+ var URI2 = `${API_URL}/core/departments/`;
5874
5911
  var DepartmentServices = {
5875
- create: (data) => FetchApi.post(`${URI}`, data),
5876
- get: (id) => FetchApi.get(`${URI}${id}/`),
5877
- list: (params) => FetchApi.get(`${URI}?${new URLSearchParams(params).toString()}`),
5878
- update: (id, data) => FetchApi.put(`${URI}${id}/`, data),
5879
- delete: (id) => FetchApi.delete(`${URI}${id}/`)
5912
+ create: (data) => FetchApi.post(`${URI2}`, data),
5913
+ get: (id) => FetchApi.get(`${URI2}${id}/`),
5914
+ list: (params) => FetchApi.get(`${URI2}?${new URLSearchParams(params).toString()}`),
5915
+ update: (id, data) => FetchApi.put(`${URI2}${id}/`, data),
5916
+ delete: (id) => FetchApi.delete(`${URI2}${id}/`)
5880
5917
  };
5881
5918
 
5882
5919
  // src/services/ProfitCostsServices.ts
5883
- var URI2 = `${API_URL}/accounting/profit-or-cost-center/`;
5920
+ var URI3 = `${API_URL}/accounting/profit-or-cost-center/`;
5884
5921
  var COST_URI = `${API_URL}/accounting/cost-center/`;
5885
5922
  var CostServices = {
5886
5923
  create: (data) => FetchApi.post(`${COST_URI}`, data),
@@ -6187,6 +6224,80 @@ var SelectCostCenter = ({
6187
6224
  loading && /* @__PURE__ */ jsx18("p", { className: "text-sm text-gray-500 mt-2", children: "Chargement des centres de co\xFBt..." })
6188
6225
  ] });
6189
6226
  };
6227
+ var SelectUnit = ({
6228
+ value,
6229
+ onSelect
6230
+ }) => {
6231
+ const { token, activeBusinessEntity } = useSession();
6232
+ const [units, setUnits] = useState13(() => {
6233
+ const cacheKey = `units_cache_${activeBusinessEntity?.id || "default"}`;
6234
+ const cached = sessionStorage.getItem(cacheKey);
6235
+ return cached ? JSON.parse(cached) : [];
6236
+ });
6237
+ const [loading, setLoading] = useState13(false);
6238
+ useEffect10(() => {
6239
+ const cacheKey = `units_cache_${activeBusinessEntity?.id || "default"}`;
6240
+ const cached = sessionStorage.getItem(cacheKey);
6241
+ if (!cached) {
6242
+ loadUnits();
6243
+ } else {
6244
+ setUnits(JSON.parse(cached));
6245
+ }
6246
+ }, [activeBusinessEntity?.id]);
6247
+ const loadUnits = async () => {
6248
+ if (!token) return;
6249
+ try {
6250
+ setLoading(true);
6251
+ const result = await UnitServices.list({ business_entity_id: activeBusinessEntity?.id });
6252
+ if (result.success) {
6253
+ setUnits(result.data);
6254
+ const cacheKey = `units_cache_${activeBusinessEntity?.id || "default"}`;
6255
+ sessionStorage.setItem(cacheKey, JSON.stringify(result.data));
6256
+ }
6257
+ } catch (error) {
6258
+ console.error(error);
6259
+ } finally {
6260
+ setLoading(false);
6261
+ }
6262
+ };
6263
+ const handleRefresh = () => {
6264
+ const cacheKey = `units_cache_${activeBusinessEntity?.id || "default"}`;
6265
+ sessionStorage.removeItem(cacheKey);
6266
+ loadUnits();
6267
+ };
6268
+ const getUnitOptions = () => {
6269
+ return units.map((unit) => ({
6270
+ value: unit.id,
6271
+ label: `${unit.code ? `[${unit.code}] ` : ""}${unit.name || "Sans nom"}`,
6272
+ object: unit,
6273
+ content: /* @__PURE__ */ jsx18("div", { className: "flex items-center space-x-3", children: /* @__PURE__ */ jsxs15("div", { className: "flex-1", children: [
6274
+ /* @__PURE__ */ jsx18("div", { className: "font-medium text-gray-900", children: unit.name || "Sans nom" }),
6275
+ unit.code && /* @__PURE__ */ jsxs15("div", { className: "text-sm text-gray-500", children: [
6276
+ "Code: ",
6277
+ unit.code
6278
+ ] }),
6279
+ unit.location && /* @__PURE__ */ jsx18("div", { className: "text-xs text-gray-400", children: unit.location })
6280
+ ] }) })
6281
+ }));
6282
+ };
6283
+ return /* @__PURE__ */ jsxs15("div", { children: [
6284
+ /* @__PURE__ */ jsx18("div", { className: "flex justify-between ", children: /* @__PURE__ */ jsx18("label", { className: "block text-sm font-medium text-gray-700 mb-2", children: "S\xE9lectionner une unit\xE9" }) }),
6285
+ /* @__PURE__ */ jsx18(
6286
+ SearchableSelect,
6287
+ {
6288
+ value,
6289
+ options: getUnitOptions(),
6290
+ placeholder: "S\xE9lectionner une unit\xE9 ...",
6291
+ searchPlaceholder: "Rechercher...",
6292
+ onSelect,
6293
+ disabled: loading,
6294
+ refresh: handleRefresh
6295
+ },
6296
+ "unit" + value
6297
+ ),
6298
+ loading && /* @__PURE__ */ jsx18("p", { className: "text-sm text-gray-500 mt-2", children: "Chargement des unit\xE9s..." })
6299
+ ] });
6300
+ };
6190
6301
 
6191
6302
  // src/components/common/Choices.tsx
6192
6303
  import { jsx as jsx19 } from "react/jsx-runtime";
@@ -7721,6 +7832,495 @@ var FileManagerContent = ({ className }) => {
7721
7832
  var FileManager = (props) => {
7722
7833
  return /* @__PURE__ */ jsx28(FileManagerProvider, { ...props, children: /* @__PURE__ */ jsx28(FileManagerContent, { className: props.className }) });
7723
7834
  };
7835
+
7836
+ // src/components/common/FileManager/components/EntityFileManager.tsx
7837
+ import React19 from "react";
7838
+ import { Loader2 as Loader22, FolderX, AlertCircle as AlertCircle4, RefreshCw as RefreshCw3 } from "lucide-react";
7839
+
7840
+ // src/components/common/FileManager/hooks/useFileManagerApi.ts
7841
+ import { useState as useState19, useEffect as useEffect13, useCallback as useCallback4 } from "react";
7842
+
7843
+ // src/components/common/FileManager/services/fileManagerApi.ts
7844
+ var API_BASE = "/api/files_manager";
7845
+ var ENTITY_MODEL_MAP = {
7846
+ CLIENT: "accounting.Client",
7847
+ VENDOR: "accounting.Vendor",
7848
+ RFQ: "procurement.RFQ",
7849
+ DEPT: "core.Departments",
7850
+ PROJECT: "core.Project"
7851
+ };
7852
+ async function apiRequest(endpoint, options = {}) {
7853
+ const url = `${API_BASE}${endpoint}`;
7854
+ const headers = {
7855
+ ...options.headers
7856
+ };
7857
+ if (!(options.body instanceof FormData)) {
7858
+ headers["Content-Type"] = "application/json";
7859
+ }
7860
+ const csrfToken = getCsrfToken();
7861
+ if (csrfToken) {
7862
+ headers["X-CSRFToken"] = csrfToken;
7863
+ }
7864
+ const response = await fetch(url, {
7865
+ ...options,
7866
+ headers,
7867
+ credentials: "include"
7868
+ // Inclure les cookies pour l'auth
7869
+ });
7870
+ if (!response.ok) {
7871
+ let errorMessage = `Erreur HTTP ${response.status}`;
7872
+ try {
7873
+ const errorData = await response.json();
7874
+ errorMessage = errorData.detail || errorData.message || errorMessage;
7875
+ } catch {
7876
+ }
7877
+ throw new Error(errorMessage);
7878
+ }
7879
+ if (response.status === 204) {
7880
+ return {};
7881
+ }
7882
+ return response.json();
7883
+ }
7884
+ function getCsrfToken() {
7885
+ const name = "csrftoken";
7886
+ const cookies = document.cookie.split(";");
7887
+ for (const cookie of cookies) {
7888
+ const trimmed = cookie.trim();
7889
+ if (trimmed.startsWith(`${name}=`)) {
7890
+ return trimmed.substring(name.length + 1);
7891
+ }
7892
+ }
7893
+ return null;
7894
+ }
7895
+ function buildQueryString(params) {
7896
+ const searchParams = new URLSearchParams();
7897
+ for (const [key, value] of Object.entries(params)) {
7898
+ if (value !== void 0 && value !== null) {
7899
+ searchParams.append(key, String(value));
7900
+ }
7901
+ }
7902
+ const queryString = searchParams.toString();
7903
+ return queryString ? `?${queryString}` : "";
7904
+ }
7905
+ async function getEntityFolders(entityType, entityId, folderType) {
7906
+ const model = ENTITY_MODEL_MAP[entityType];
7907
+ if (!model) {
7908
+ throw new Error(`Type d'entit\xE9 non support\xE9: ${entityType}`);
7909
+ }
7910
+ const params = buildQueryString({
7911
+ model,
7912
+ id: entityId,
7913
+ type: folderType
7914
+ });
7915
+ return apiRequest(`/folders/for_entity/${params}`);
7916
+ }
7917
+ async function getFolderTree(folderCode) {
7918
+ return apiRequest(`/folders/${folderCode}/tree/`);
7919
+ }
7920
+ async function getFolder(folderCode) {
7921
+ return apiRequest(`/folders/${folderCode}/`);
7922
+ }
7923
+ async function createFolder(name, parentCode, businessEntityId) {
7924
+ return apiRequest("/folders/", {
7925
+ method: "POST",
7926
+ body: JSON.stringify({
7927
+ name,
7928
+ parent: parentCode,
7929
+ business_entity: businessEntityId,
7930
+ folder_type: "MISC",
7931
+ // Type par défaut pour les dossiers créés manuellement
7932
+ is_system_folder: false
7933
+ })
7934
+ });
7935
+ }
7936
+ async function renameFolder(folderCode, newName) {
7937
+ return apiRequest(`/folders/${folderCode}/`, {
7938
+ method: "PATCH",
7939
+ body: JSON.stringify({ name: newName })
7940
+ });
7941
+ }
7942
+ async function deleteFolder(folderCode) {
7943
+ await apiRequest(`/folders/${folderCode}/`, {
7944
+ method: "DELETE"
7945
+ });
7946
+ }
7947
+ async function getFolderFiles(folderId) {
7948
+ const params = buildQueryString({ folder: folderId });
7949
+ return apiRequest(`/files/${params}`);
7950
+ }
7951
+ async function uploadFile(file, folderCode, options = {}) {
7952
+ const formData = new FormData();
7953
+ formData.append("file", file);
7954
+ formData.append("folder_id", folderCode);
7955
+ if (options.name) {
7956
+ formData.append("name", options.name);
7957
+ }
7958
+ if (options.description) {
7959
+ formData.append("description", options.description);
7960
+ }
7961
+ if (options.tags && options.tags.length > 0) {
7962
+ formData.append("tags", JSON.stringify(options.tags));
7963
+ }
7964
+ return apiRequest("/files/", {
7965
+ method: "POST",
7966
+ body: formData
7967
+ });
7968
+ }
7969
+ async function uploadFiles(files, folderCode) {
7970
+ const results = [];
7971
+ for (const file of files) {
7972
+ const uploaded = await uploadFile(file, folderCode);
7973
+ results.push(uploaded);
7974
+ }
7975
+ return results;
7976
+ }
7977
+ async function renameFile(fileCode, newName) {
7978
+ return apiRequest(`/files/${fileCode}/`, {
7979
+ method: "PATCH",
7980
+ body: JSON.stringify({ name: newName })
7981
+ });
7982
+ }
7983
+ async function deleteFile(fileCode) {
7984
+ await apiRequest(`/files/${fileCode}/`, {
7985
+ method: "DELETE"
7986
+ });
7987
+ }
7988
+ function getDownloadUrl(fileCode) {
7989
+ return `${API_BASE}/files/${fileCode}/download/`;
7990
+ }
7991
+ function getFileUrl(fileUrl) {
7992
+ if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
7993
+ return fileUrl;
7994
+ }
7995
+ return fileUrl.startsWith("/") ? fileUrl : `/${fileUrl}`;
7996
+ }
7997
+ var fileManagerApi = {
7998
+ // Dossiers
7999
+ getEntityFolders,
8000
+ getFolderTree,
8001
+ getFolder,
8002
+ createFolder,
8003
+ renameFolder,
8004
+ deleteFolder,
8005
+ // Fichiers
8006
+ getFolderFiles,
8007
+ uploadFile,
8008
+ uploadFiles,
8009
+ renameFile,
8010
+ deleteFile,
8011
+ // URLs
8012
+ getDownloadUrl,
8013
+ getFileUrl,
8014
+ // Helpers exposés
8015
+ ENTITY_MODEL_MAP
8016
+ };
8017
+
8018
+ // src/components/common/FileManager/hooks/useFileManagerApi.ts
8019
+ function transformFile(backendFile, parentPath, parentId) {
8020
+ return {
8021
+ id: backendFile.code,
8022
+ name: backendFile.name,
8023
+ type: "file",
8024
+ mimeType: backendFile.mime_type,
8025
+ size: backendFile.size,
8026
+ path: `${parentPath}/${backendFile.name}`,
8027
+ parentId,
8028
+ url: fileManagerApi.getFileUrl(backendFile.file_url),
8029
+ createdAt: backendFile.created_at,
8030
+ updatedAt: backendFile.updated_at,
8031
+ metadata: {
8032
+ extension: backendFile.extension,
8033
+ folderId: backendFile.folder
8034
+ }
8035
+ };
8036
+ }
8037
+ function transformFolder(backendFolder, parentPath = "", parentId = null) {
8038
+ const currentPath = parentPath ? `${parentPath}/${backendFolder.name}` : `/${backendFolder.name}`;
8039
+ const childFolders = (backendFolder.children || []).map(
8040
+ (child) => transformFolder(child, currentPath, backendFolder.code)
8041
+ );
8042
+ const childFiles = (backendFolder.files || []).map(
8043
+ (file) => transformFile(file, currentPath, backendFolder.code)
8044
+ );
8045
+ return {
8046
+ id: backendFolder.code,
8047
+ name: backendFolder.name,
8048
+ type: "folder",
8049
+ path: currentPath,
8050
+ parentId,
8051
+ children: [...childFolders, ...childFiles],
8052
+ metadata: {
8053
+ folderType: backendFolder.folder_type,
8054
+ folderTypeDisplay: backendFolder.folder_type_display,
8055
+ categoryType: backendFolder.category_type,
8056
+ isSystemFolder: backendFolder.is_system_folder,
8057
+ fileCount: backendFolder.file_count,
8058
+ totalSize: backendFolder.total_size
8059
+ }
8060
+ };
8061
+ }
8062
+ function extractRootChildren(entityFolder) {
8063
+ return entityFolder.children || [];
8064
+ }
8065
+ function useFileManagerApi(entityType, entityId, businessEntityId) {
8066
+ const [data, setData] = useState19([]);
8067
+ const [loading, setLoading] = useState19(true);
8068
+ const [error, setError] = useState19(null);
8069
+ const [rootFolderCode, setRootFolderCode] = useState19(null);
8070
+ const loadEntityFolders = useCallback4(async () => {
8071
+ setLoading(true);
8072
+ setError(null);
8073
+ try {
8074
+ const folders = await fileManagerApi.getEntityFolders(entityType, entityId);
8075
+ if (!folders || folders.length === 0) {
8076
+ setData([]);
8077
+ setRootFolderCode(null);
8078
+ setLoading(false);
8079
+ return;
8080
+ }
8081
+ const entityFolder = folders.find((f) => f.folder_type === "ENTITY");
8082
+ if (!entityFolder) {
8083
+ console.warn("Dossier ENTITY non trouv\xE9, utilisation du premier dossier");
8084
+ const firstFolder = folders[0];
8085
+ setRootFolderCode(firstFolder.code);
8086
+ const tree = await fileManagerApi.getFolderTree(firstFolder.code);
8087
+ const transformed = transformFolder(tree);
8088
+ setData(extractRootChildren(transformed));
8089
+ } else {
8090
+ setRootFolderCode(entityFolder.code);
8091
+ const tree = await fileManagerApi.getFolderTree(entityFolder.code);
8092
+ const transformed = transformFolder(tree);
8093
+ setData(extractRootChildren(transformed));
8094
+ }
8095
+ } catch (err) {
8096
+ console.error("Erreur lors du chargement des dossiers:", err);
8097
+ setError(err instanceof Error ? err : new Error(String(err)));
8098
+ setData([]);
8099
+ } finally {
8100
+ setLoading(false);
8101
+ }
8102
+ }, [entityType, entityId]);
8103
+ const refresh = useCallback4(async () => {
8104
+ await loadEntityFolders();
8105
+ }, [loadEntityFolders]);
8106
+ useEffect13(() => {
8107
+ loadEntityFolders();
8108
+ }, [loadEntityFolders]);
8109
+ const handleCreateFolder = useCallback4(
8110
+ async (name, parentId) => {
8111
+ try {
8112
+ const created = await fileManagerApi.createFolder(
8113
+ name,
8114
+ parentId,
8115
+ businessEntityId
8116
+ );
8117
+ await refresh();
8118
+ return transformFolder(created, "", parentId);
8119
+ } catch (err) {
8120
+ console.error("Erreur lors de la cr\xE9ation du dossier:", err);
8121
+ setError(err instanceof Error ? err : new Error(String(err)));
8122
+ throw err;
8123
+ }
8124
+ },
8125
+ [businessEntityId, refresh]
8126
+ );
8127
+ const handleUploadFiles = useCallback4(
8128
+ async (files, parentId) => {
8129
+ try {
8130
+ const uploaded = await fileManagerApi.uploadFiles(files, parentId);
8131
+ await refresh();
8132
+ return uploaded.map((file) => transformFile(file, "", parentId));
8133
+ } catch (err) {
8134
+ console.error("Erreur lors de l'upload:", err);
8135
+ setError(err instanceof Error ? err : new Error(String(err)));
8136
+ throw err;
8137
+ }
8138
+ },
8139
+ [refresh]
8140
+ );
8141
+ const handleRename = useCallback4(
8142
+ async (item, newName) => {
8143
+ try {
8144
+ if (item.type === "folder") {
8145
+ await fileManagerApi.renameFolder(item.id, newName);
8146
+ } else {
8147
+ await fileManagerApi.renameFile(item.id, newName);
8148
+ }
8149
+ await refresh();
8150
+ } catch (err) {
8151
+ console.error("Erreur lors du renommage:", err);
8152
+ setError(err instanceof Error ? err : new Error(String(err)));
8153
+ throw err;
8154
+ }
8155
+ },
8156
+ [refresh]
8157
+ );
8158
+ const handleDelete = useCallback4(
8159
+ async (item) => {
8160
+ try {
8161
+ if (item.metadata?.isSystemFolder) {
8162
+ throw new Error("Les dossiers syst\xE8me ne peuvent pas \xEAtre supprim\xE9s");
8163
+ }
8164
+ if (item.type === "folder") {
8165
+ await fileManagerApi.deleteFolder(item.id);
8166
+ } else {
8167
+ await fileManagerApi.deleteFile(item.id);
8168
+ }
8169
+ await refresh();
8170
+ } catch (err) {
8171
+ console.error("Erreur lors de la suppression:", err);
8172
+ setError(err instanceof Error ? err : new Error(String(err)));
8173
+ throw err;
8174
+ }
8175
+ },
8176
+ [refresh]
8177
+ );
8178
+ const handleDownload = useCallback4((item) => {
8179
+ if (item.type === "file") {
8180
+ const url = item.url || fileManagerApi.getDownloadUrl(item.id);
8181
+ window.open(url, "_blank");
8182
+ }
8183
+ }, []);
8184
+ return {
8185
+ data,
8186
+ loading,
8187
+ error,
8188
+ rootFolderCode,
8189
+ refresh,
8190
+ handlers: {
8191
+ onCreateFolder: handleCreateFolder,
8192
+ onUploadFiles: handleUploadFiles,
8193
+ onRename: handleRename,
8194
+ onDelete: handleDelete,
8195
+ onDownload: handleDownload
8196
+ }
8197
+ };
8198
+ }
8199
+
8200
+ // src/components/common/FileManager/components/EntityFileManager.tsx
8201
+ import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
8202
+ function getRootName(entityType) {
8203
+ const names = {
8204
+ CLIENT: "Documents Client",
8205
+ VENDOR: "Documents Fournisseur",
8206
+ RFQ: "Documents RFQ",
8207
+ DEPT: "Documents D\xE9partement",
8208
+ PROJECT: "Documents Projet"
8209
+ };
8210
+ return names[entityType] || "Documents";
8211
+ }
8212
+ var LoadingState = ({ height = "400px" }) => /* @__PURE__ */ jsxs24(
8213
+ "div",
8214
+ {
8215
+ className: "flex flex-col items-center justify-center bg-gray-50 dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-800",
8216
+ style: { height },
8217
+ children: [
8218
+ /* @__PURE__ */ jsx29(Loader22, { className: "w-10 h-10 text-blue-500 animate-spin mb-4" }),
8219
+ /* @__PURE__ */ jsx29("p", { className: "text-gray-600 dark:text-gray-400 text-sm", children: "Chargement des documents..." })
8220
+ ]
8221
+ }
8222
+ );
8223
+ var ErrorState = ({ error, onRetry, height = "400px" }) => /* @__PURE__ */ jsxs24(
8224
+ "div",
8225
+ {
8226
+ className: "flex flex-col items-center justify-center bg-red-50 dark:bg-red-900/20 rounded-xl border border-red-200 dark:border-red-800",
8227
+ style: { height },
8228
+ children: [
8229
+ /* @__PURE__ */ jsx29(AlertCircle4, { className: "w-10 h-10 text-red-500 mb-4" }),
8230
+ /* @__PURE__ */ jsx29("p", { className: "text-red-600 dark:text-red-400 text-sm font-medium mb-2", children: "Erreur lors du chargement" }),
8231
+ /* @__PURE__ */ jsx29("p", { className: "text-red-500 dark:text-red-400 text-xs mb-4 max-w-md text-center px-4", children: error.message }),
8232
+ /* @__PURE__ */ jsxs24(
8233
+ "button",
8234
+ {
8235
+ onClick: onRetry,
8236
+ className: "flex items-center gap-2 px-4 py-2 bg-red-100 dark:bg-red-900/40 text-red-700 dark:text-red-300 rounded-lg hover:bg-red-200 dark:hover:bg-red-900/60 transition-colors text-sm",
8237
+ children: [
8238
+ /* @__PURE__ */ jsx29(RefreshCw3, { className: "w-4 h-4" }),
8239
+ "R\xE9essayer"
8240
+ ]
8241
+ }
8242
+ )
8243
+ ]
8244
+ }
8245
+ );
8246
+ var EmptyState = ({ height = "400px" }) => /* @__PURE__ */ jsxs24(
8247
+ "div",
8248
+ {
8249
+ className: "flex flex-col items-center justify-center bg-gray-50 dark:bg-gray-900 rounded-xl border border-gray-200 dark:border-gray-800",
8250
+ style: { height },
8251
+ children: [
8252
+ /* @__PURE__ */ jsx29(FolderX, { className: "w-10 h-10 text-gray-400 mb-4" }),
8253
+ /* @__PURE__ */ jsx29("p", { className: "text-gray-600 dark:text-gray-400 text-sm font-medium mb-1", children: "Aucun document trouv\xE9" }),
8254
+ /* @__PURE__ */ jsx29("p", { className: "text-gray-500 dark:text-gray-500 text-xs", children: "La structure de dossiers n'a pas encore \xE9t\xE9 cr\xE9\xE9e pour cette entit\xE9." })
8255
+ ]
8256
+ }
8257
+ );
8258
+ var EntityFileManager = ({
8259
+ entityType,
8260
+ entityId,
8261
+ businessEntityId,
8262
+ className,
8263
+ height = "600px",
8264
+ // Permissions (défaut: tout autorisé)
8265
+ allowUpload = true,
8266
+ allowCreateFolder = true,
8267
+ allowRename = true,
8268
+ allowDelete = true,
8269
+ allowDownload = true,
8270
+ // Callbacks
8271
+ onFileSelect,
8272
+ onFileOpen,
8273
+ onUploadSuccess,
8274
+ onError
8275
+ }) => {
8276
+ const { data, loading, error, refresh, handlers } = useFileManagerApi(
8277
+ entityType,
8278
+ entityId,
8279
+ businessEntityId
8280
+ );
8281
+ React19.useEffect(() => {
8282
+ if (error && onError) {
8283
+ onError(error);
8284
+ }
8285
+ }, [error, onError]);
8286
+ if (loading) {
8287
+ return /* @__PURE__ */ jsx29(LoadingState, { height });
8288
+ }
8289
+ if (error) {
8290
+ return /* @__PURE__ */ jsx29(ErrorState, { error, onRetry: refresh, height });
8291
+ }
8292
+ if (!data || data.length === 0) {
8293
+ return /* @__PURE__ */ jsx29(EmptyState, { height });
8294
+ }
8295
+ const handleUploadFiles = async (files, parentId) => {
8296
+ const result = await handlers.onUploadFiles(files, parentId);
8297
+ if (result && onUploadSuccess) {
8298
+ onUploadSuccess(result);
8299
+ }
8300
+ return result;
8301
+ };
8302
+ return /* @__PURE__ */ jsx29("div", { className: cn("relative", className), style: { height }, children: /* @__PURE__ */ jsx29(
8303
+ FileManager,
8304
+ {
8305
+ data,
8306
+ rootName: getRootName(entityType),
8307
+ onCreateFolder: handlers.onCreateFolder,
8308
+ onUploadFiles: handleUploadFiles,
8309
+ onRename: handlers.onRename,
8310
+ onDelete: handlers.onDelete,
8311
+ onDownload: handlers.onDownload,
8312
+ onSelect: onFileSelect,
8313
+ onOpen: onFileOpen,
8314
+ allowUpload,
8315
+ allowCreateFolder,
8316
+ allowRename,
8317
+ allowDelete,
8318
+ allowDownload,
8319
+ allowMultiSelect: true,
8320
+ className: "h-full"
8321
+ }
8322
+ ) });
8323
+ };
7724
8324
  export {
7725
8325
  Alert_default as Alert,
7726
8326
  AlertProvider,
@@ -7733,6 +8333,7 @@ export {
7733
8333
  Choices_default as CHOICES,
7734
8334
  CountrySelector,
7735
8335
  DateInput,
8336
+ EntityFileManager,
7736
8337
  FDrawer,
7737
8338
  FetchApi,
7738
8339
  FileInput,
@@ -7752,6 +8353,7 @@ export {
7752
8353
  SelectCostCenter,
7753
8354
  SelectDepartment,
7754
8355
  SelectInput,
8356
+ SelectUnit,
7755
8357
  SelectUser,
7756
8358
  SelectVendor,
7757
8359
  SessionProvider,
@@ -7761,12 +8363,15 @@ export {
7761
8363
  ThemeContext_default as ThemeProvider,
7762
8364
  Toast_default as ToastContainer,
7763
8365
  ToastProvider,
8366
+ UnitServices,
7764
8367
  UserServices,
8368
+ fileManagerApi,
7765
8369
  formatDate,
7766
8370
  formatFileSize,
7767
8371
  getFileIcon,
7768
8372
  useAlert,
7769
8373
  useFileManager,
8374
+ useFileManagerApi,
7770
8375
  useSession,
7771
8376
  useToast
7772
8377
  };