ptechcore_ui 0.0.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.
Files changed (57) hide show
  1. package/eslint.config.js +28 -0
  2. package/index.html +78 -0
  3. package/package.json +42 -0
  4. package/postcss.config.js +6 -0
  5. package/src/App.tsx +156 -0
  6. package/src/assets/imgs/login_illustration.png +0 -0
  7. package/src/components/common/Buttons.tsx +39 -0
  8. package/src/components/common/Cards.tsx +18 -0
  9. package/src/components/common/FDrawer.tsx +2448 -0
  10. package/src/components/common/FDrawer.types.ts +191 -0
  11. package/src/components/common/Inputs.tsx +409 -0
  12. package/src/components/common/Modals.tsx +41 -0
  13. package/src/components/common/Navigations.tsx +0 -0
  14. package/src/components/common/Toast.tsx +0 -0
  15. package/src/components/demo/ToastDemo.tsx +73 -0
  16. package/src/components/layout/Header.tsx +202 -0
  17. package/src/components/layout/ModernDoubleSidebarLayout.tsx +727 -0
  18. package/src/components/layout/PrivateLayout.tsx +52 -0
  19. package/src/components/layout/Sidebar.tsx +182 -0
  20. package/src/components/ui/Toast.tsx +93 -0
  21. package/src/contexts/SessionContext.tsx +77 -0
  22. package/src/contexts/ThemeContext.tsx +58 -0
  23. package/src/contexts/ToastContext.tsx +94 -0
  24. package/src/index.css +3 -0
  25. package/src/main.tsx +10 -0
  26. package/src/models/Organization.ts +47 -0
  27. package/src/models/Plan.ts +42 -0
  28. package/src/models/User.ts +23 -0
  29. package/src/pages/Analytics.tsx +101 -0
  30. package/src/pages/CreateOrganization.tsx +215 -0
  31. package/src/pages/Dashboard.tsx +15 -0
  32. package/src/pages/Home.tsx +12 -0
  33. package/src/pages/Profile.tsx +313 -0
  34. package/src/pages/Settings.tsx +382 -0
  35. package/src/pages/Team.tsx +180 -0
  36. package/src/pages/auth/Login.tsx +140 -0
  37. package/src/pages/auth/Register.tsx +302 -0
  38. package/src/pages/organizations/DetailEntity.tsx +1002 -0
  39. package/src/pages/organizations/DetailOrganizations.tsx +1629 -0
  40. package/src/pages/organizations/ListOrganizations.tsx +270 -0
  41. package/src/pages/pricings/CartPlan.tsx +486 -0
  42. package/src/pages/pricings/ListPricing.tsx +321 -0
  43. package/src/pages/users/CreateUser.tsx +450 -0
  44. package/src/pages/users/ListUsers.tsx +0 -0
  45. package/src/services/AuthServices.ts +94 -0
  46. package/src/services/OrganizationServices.ts +61 -0
  47. package/src/services/PlanSubscriptionServices.tsx +137 -0
  48. package/src/services/UserServices.ts +36 -0
  49. package/src/services/api.ts +64 -0
  50. package/src/styles/theme.ts +383 -0
  51. package/src/utils/utils.ts +48 -0
  52. package/src/vite-env.d.ts +1 -0
  53. package/tailwind.config.js +158 -0
  54. package/tsconfig.app.json +24 -0
  55. package/tsconfig.json +7 -0
  56. package/tsconfig.node.json +22 -0
  57. package/vite.config.ts +10 -0
@@ -0,0 +1,2448 @@
1
+ import axios from 'axios';
2
+ import React, { useEffect, useMemo, useRef, useState, ReactNode } from 'react';
3
+ import {
4
+ FDrawerProps,
5
+ FooterSummaryProps,
6
+ DropdownActionsMenuProps,
7
+ TableCollapseSimpleProps,
8
+ FTableProps,
9
+ DynamicTableProps,
10
+ ReponseDetail,
11
+ FDrawerHeader,
12
+ Filter,
13
+ ActionItem
14
+ } from './FDrawer.types';
15
+ // import ALL_FORMS from '../../modules/admin/crm/views/forms/allform';
16
+ import { addressIpApi, authenticated_header } from '../../globals/api';
17
+
18
+
19
+ import {
20
+ bouttonDrawerAdd,
21
+ activeBouttonDrawerAdd,
22
+ activeBouttonDrawerFilter,
23
+ activeBouttonDrawerSearch,
24
+ bouttonDrawerFilter,
25
+ bouttonDrawerSearch,
26
+ bouttonDrawerValidateSearch,
27
+ activebouttonDrawerIssuePayment,
28
+ bouttonDrawerIssuePayment,
29
+ bouttonDrawerCreateTemplate,
30
+ activebouttonDrawerCreateTemplate,
31
+ activebouttonDrawerNewEntry,
32
+ bouttonDrawerNewEntry,
33
+ activebouttonDrawerReceivePayment,
34
+ bouttonDrawerReceivePayment,
35
+ activebouttonDrawerInputTemplate,
36
+ bouttonDrawerInputTemplate,
37
+ activeBouttonDrawerImportation_cancel,
38
+ activeBouttonDrawerImportation,
39
+ bouttonDrawerImportation_cancel,
40
+ bouttonDrawerImportation,
41
+ activebouttonDrawerExcelExport,
42
+ bouttonDrawerexcelExport,
43
+ bouttonDrawerCancel,
44
+ activebouttonDrawerCancel,
45
+
46
+ bouttonDrawerPrevisualisation,
47
+ activebouttonDrawerPrevisualisation,
48
+ bouttonDrawerPrinter,
49
+ activebouttonDrawerPrinter,
50
+ bouttonDrawerSubmit,
51
+ activebouttonDrawerSubmit,
52
+ bouttonDrawerAttachement,
53
+ activebouttonDrawerAttachement,
54
+ bouttonDrawerMessagerie,
55
+ activebouttonDrawerMessagerie,
56
+ bouttonDrawerEnregistrer,
57
+ activebouttonDrawerEnregistrer,
58
+ activebouttonDrawerExcelImport,
59
+ bouttonDrawerexcelImport,
60
+ table_com_icon,
61
+ table_com_icon2,
62
+ activebouttonDrawerConsult,
63
+ bouttonDrawerConsult,
64
+ bouttonDrawerattachHaving,
65
+ activebouttonDrawerAttachHaving,
66
+ bouttonDrawerAttachLight,
67
+ activebouttonDrawerAttachLight
68
+ } from "../../assets/images/ExportImage";
69
+ import { useLoading } from '../../globals/globals_loading';
70
+ import { Link, useLocation, useNavigate, useSearchParams } from 'react-router-dom';
71
+ import { useCenter } from '../footer/Footer';
72
+ import { DICTIONNARY, useLanguage } from '../../globals/dictionnary';
73
+ import { FilterPagination } from '../pagination';
74
+ import { DATETIME } from '../../utils/dates';
75
+ import { CustomSelector } from '../CustomSelector';
76
+ import { nodata_illus } from '../../assets/images/ExportImage';
77
+ import { NewButton } from '../buttons/buttons';
78
+ import { useDownloadExcel } from 'react-export-table-to-excel';
79
+ import { FModal, ModelTemplapleSimpleV1 } from '../modal/ModalGlobalTemplate';
80
+ import { CommunicationHistoryComponent } from '../CommunicationHistory';
81
+ import { Attachments } from '../attachment/Attachment';
82
+ import { DynamicTable, TableBasic, TableCollapseSimple, TableWithCheckbox } from './fTables';
83
+ import { CheckboxInput } from '../form-inputs/FormInputs';
84
+ import { LuSearch, LuSearchX, LuPlusCircle } from 'react-icons/lu';
85
+ import { sep_mil } from '../../modules/admin/recovery/views/constants/choices_handle';
86
+ import { FDropdown } from './fUI';
87
+ import { IoCloseCircleSharp } from 'react-icons/io5';
88
+ import { PiColumnsFill } from 'react-icons/pi';
89
+ import { CustomFieldset } from '../custom-card/CustomCard';
90
+ import { CiBank } from 'react-icons/ci';
91
+ import { ExcelImportModal } from './ExcelImportModal';
92
+
93
+
94
+ export const FDrawer: React.FC<FDrawerProps> = (props) => {
95
+
96
+
97
+
98
+
99
+ // ---------------------------------------------------------------------------
100
+ // ---------------------------------CUSTOM HOOKS------------------------------
101
+ // ---------------------------------------------------------------------------
102
+
103
+ const navigate = useNavigate();
104
+ const [searchParams] = useSearchParams();
105
+ const location = useLocation();
106
+
107
+ const allParams = Object.fromEntries([...searchParams]);
108
+ const { center, dateRange, changeRange, financialYear, dateSystem } = useCenter();
109
+ const { language } = useLanguage();
110
+ const { setIsLoadingCancelable } = useLoading()
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // ---------------------------------------------------------------------------
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // ---------------------------------REFS--------------------------------------
117
+ // ---------------------------------------------------------------------------
118
+
119
+ const champsRef = useRef([]);
120
+ const champsRef2 = useRef([]);
121
+ const searchSelect = useRef();
122
+ const btnsContainer = useRef();
123
+ const sweetAlertRef = useRef();
124
+ const tableRefExportExcel = useRef(null);
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // ---------------------------------------------------------------------------
128
+
129
+
130
+
131
+
132
+
133
+ // ---------------------------------------------------------------------------
134
+ // ---------------------------------DEFAULT PROPS-----------------------------
135
+ // ---------------------------------------------------------------------------
136
+
137
+ const selected = props.selected ?? []
138
+
139
+ const maxText = props.maxText ?? 75
140
+
141
+ const type = props.type ?? "table"
142
+
143
+ const noFilter = props.noFilter ?? false
144
+
145
+ const noSearch = props.noSearch ?? false
146
+ const canExport = props.canExport ?? false
147
+
148
+ const table_type = props.table_type ?? "basic"
149
+
150
+ const rerender = props.toggle ?? "";
151
+
152
+ const headers = props.headers ?? [];
153
+
154
+ const totalHeaders = props.totalHeaders ?? [];
155
+
156
+ const totalMainHeaders = props.totalMainHeaders ?? [];
157
+
158
+ const btnsList = props.btnsList ?? [];
159
+
160
+ const infinite_scroll = props.infinite_scroll ?? false;
161
+
162
+ const title_ = (props.title && typeof (props.title) === "string") ? props.title as string : props.title_;
163
+
164
+ // ---------------------------------------------------------------------------
165
+ // ---------------------------------------------------------------------------
166
+
167
+
168
+
169
+
170
+ const [showPeriodModal, setShowPeriodModal] = useState(false);
171
+
172
+
173
+ // ---------------------------------------------------------------------------
174
+ // ---------------------------------STATES-----------------------------------
175
+ // ---------------------------------------------------------------------------
176
+
177
+ const [lineSelected, setLineSelected] = useState<string>('')
178
+ const [showContent, setShowContent] = useState<Record<string, boolean>>({});
179
+ const [showModalComment, setShowModalComment] = useState<boolean>(false);
180
+ const [secondInfoComment, setSecondInfoComment] = useState<string>("");
181
+ const [comment, setComment] = useState<any>(false);
182
+ const [showLeadForm, setShowLeadForm] = useState<boolean>(false);
183
+ const [isSorting, setIsSorting] = useState<string | undefined>(props.orderByDefault);
184
+ const [data, setData] = useState<any[]>([]);
185
+ const [dataFilters, setDataFilters] = useState<any[]>([]);
186
+ const [noFiltres, setNoFiltres] = useState<boolean>(false);
187
+ const [showFiltres, setShowFiltres] = useState<boolean>(false);
188
+ const [showRecherche, setShowRecherche] = useState<boolean>(false);
189
+ const [showFR, setShowFR] = useState<string>("");
190
+ const [filtres, setFiltres] = useState<Filter[]>([]);
191
+ const [csv, setCsv] = useState<string>("");
192
+ const [legends, setLegends] = useState<any[]>([]);
193
+ const [listFiltres, setListFiltres] = useState<any[]>([]);
194
+ const [loading, setLoading] = useState<boolean>(true);
195
+ const [loadingIS, setLoadingIS] = useState<boolean>(true);
196
+ const [loading2, setLoading2] = useState<boolean>(true);
197
+ const [queryURL, setQueryURL] = useState<string>('');
198
+ const [frontqueryURL, setFrontQueryURL] = useState<string>('');
199
+ const [showCommunication, setShowCommunication] = useState<boolean>(false);
200
+ const [showAttachments, setShowAttachments] = useState<boolean>(false);
201
+ const [showImportExcel, setShowImportExcel] = useState<boolean>(false);
202
+ const [attachments, setAttachments] = useState<any[]>([])
203
+ const [search, setSearch] = useState<string>('');
204
+ const [selectedItems, setSelectedItems] = useState<string[]>([]);
205
+ const [btnAdd, setBtnAdd] = useState<boolean>(true);
206
+ const [reponseDetail, setReponseDetail] = useState<ReponseDetail>({
207
+ total_pages: null,
208
+ previous: null,
209
+ next: null,
210
+ current_page: null,
211
+
212
+ });
213
+
214
+ const makeFilters = (): Record<string, string> => {
215
+ const newFilters = headers.reduce<Record<string, string>>((acc, item) => {
216
+ if (item.formule) {
217
+ acc[`${item.search_name}__icontains`] = ""
218
+ return acc
219
+ } else {
220
+ acc[`${item.key}__icontains`] = ""
221
+ return acc
222
+ }
223
+ }, {})
224
+ return newFilters
225
+ }
226
+ const preFilters = (headers.length) > 0 ? makeFilters() : {}
227
+
228
+ // Définir les filtres initiaux en fonction de la présence de paramètres dans l'URL
229
+ const [filters, setFilters] = useState<Record<string, any>>(() => {
230
+ if (Object.keys(allParams).length > 0) {
231
+ return allParams; // Si des paramètres existent, on les utilise comme filtres
232
+ } else {
233
+ return {
234
+ ...preFilters,
235
+ center_id: center?.id ?? null,
236
+ order_by: props.orderByDefault ?? "id",
237
+ page: 1
238
+ }
239
+ }
240
+ });
241
+
242
+ const [filtersMemo, setFiltersMemo] = useState<Record<string, any>>({
243
+ ...preFilters,
244
+ center_id: center?.id ?? null,
245
+ order_by: props.orderByDefault ?? "id",
246
+ page: 1
247
+ })
248
+
249
+ // ---------------------------------------------------------------------------
250
+ // ---------------------------------------------------------------------------
251
+
252
+
253
+
254
+
255
+ // ---------------------------------------------------------------------------
256
+ // ---------------------------------CONSTANTES--------------------------------
257
+ // ---------------------------------------------------------------------------
258
+
259
+ const drawerActions = {
260
+ "add": {
261
+ icon: [bouttonDrawerAdd, activeBouttonDrawerAdd],
262
+ label: DICTIONNARY.Add[language]
263
+ },
264
+ "create-template": {
265
+ icon: [bouttonDrawerCreateTemplate, activebouttonDrawerCreateTemplate],
266
+ label: DICTIONNARY.CréerUnModèleDeSaisie[language]
267
+ },
268
+ "issue-payment": {
269
+ icon: [bouttonDrawerIssuePayment, activebouttonDrawerIssuePayment],
270
+ label: DICTIONNARY.EmettreUnRéglement[language]
271
+ },
272
+ "receive-payment": {
273
+ icon: [bouttonDrawerReceivePayment, activebouttonDrawerReceivePayment],
274
+ label: DICTIONNARY.RecevoirUnRéglement[language]
275
+ },
276
+ "input-template": {
277
+ icon: [bouttonDrawerInputTemplate, activebouttonDrawerInputTemplate],
278
+ label: DICTIONNARY.SélectionnerUnModèleDeSaisie[language]
279
+ },
280
+ "import-new-report": {
281
+ icon: [bouttonDrawerImportation, activeBouttonDrawerImportation],
282
+ label: DICTIONNARY.ImportReport[language]
283
+ },
284
+ "import-cancel-new-report": {
285
+ icon: [bouttonDrawerImportation_cancel, activeBouttonDrawerImportation_cancel],
286
+ label: DICTIONNARY.CancelImportReport[language]
287
+ },
288
+ "cancel": {
289
+ icon: [bouttonDrawerCancel, activebouttonDrawerCancel],
290
+ label: DICTIONNARY.Cancel[language]
291
+ },
292
+ // "edit": {
293
+ // icon: [bouttonDrawerEdit, activebouttonDrawerEdit],
294
+ // label: DICTIONNARY.Edit[language]
295
+ // },
296
+ "previsualisation": {
297
+ icon: [bouttonDrawerPrevisualisation, activebouttonDrawerPrevisualisation],
298
+ label: DICTIONNARY.Previews[language]
299
+ },
300
+ "submit": {
301
+ icon: [bouttonDrawerSubmit, activebouttonDrawerSubmit],
302
+ label: DICTIONNARY.Soumettre[language]
303
+ },
304
+ "send": {
305
+ icon: [<RiMailSendFill />],
306
+ label: DICTIONNARY.Send[language]
307
+ },
308
+ "new-entry": {
309
+ icon: [bouttonDrawerNewEntry, activebouttonDrawerNewEntry],
310
+ label: DICTIONNARY.NouvelleSaisie[language]
311
+ },
312
+ "print": {
313
+ icon: [bouttonDrawerPrinter, activebouttonDrawerPrinter],
314
+ label: DICTIONNARY.Print[language]
315
+ },
316
+ "mark-as-signed": {
317
+ icon: [<FaFileSignature />],
318
+ label: DICTIONNARY.MarkAsSigned[language]
319
+ },
320
+ "closed": {
321
+ icon: [<FaStamp />],
322
+ label: DICTIONNARY.Closing[language]
323
+ },
324
+ "export-excel": {
325
+ icon: [<FaFileExport />],
326
+ label: DICTIONNARY.ExportExcel[language]
327
+ },
328
+ "save": {
329
+ icon: [bouttonDrawerEnregistrer, activebouttonDrawerEnregistrer],
330
+ label: DICTIONNARY.Save[language]
331
+ },
332
+ "attach-having": {
333
+ icon: [bouttonDrawerattachHaving, activebouttonDrawerAttachHaving],
334
+ label: DICTIONNARY.LinkCreditMemoInvoices[language]
335
+ },
336
+ "attach-having-transfer": {
337
+ icon: [bouttonDrawerAttachLight, activebouttonDrawerAttachLight],
338
+ label: DICTIONNARY.LinkingInvoicesToTheAccounts[language]
339
+ },
340
+ }
341
+
342
+ // ---------------------------------------------------------------------------
343
+ // ---------------------------------------------------------------------------
344
+
345
+
346
+
347
+
348
+ // ---------------------------------------------------------------------------
349
+ // ---------------------------------FONCTIONS---------------------------------
350
+ // ---------------------------------------------------------------------------
351
+
352
+ const toggleContent = (propertyCode) => {
353
+ setShowContent((prevShowContent) => {
354
+ const newShowContent = {
355
+ ...prevShowContent,
356
+ [propertyCode]: !prevShowContent[propertyCode],
357
+ };
358
+ return newShowContent;
359
+ });
360
+ };
361
+
362
+ const handleFilterChange = (event) => {
363
+ const { name, value } = event.target;
364
+ setFilters({ ...filters, [name]: value });
365
+ }
366
+ const handleFilterFilterChange = (event) => {
367
+ const { name, value } = event.target;
368
+ setFilters({ ...filters, search_type: 'filter', [name]: value });
369
+
370
+ }
371
+
372
+ const handleRemoveItem = (itemToRemove) => {
373
+ setSelectedItems(prevItems => prevItems.filter(item => item !== itemToRemove));
374
+ };
375
+
376
+ const handleSelectedItem = (event) => {
377
+ const selectedItem = event.target.value;
378
+ setSelectedItems(prevItems => {
379
+ if (!prevItems.includes(selectedItem)) {
380
+ return [...prevItems, selectedItem];
381
+ }
382
+ return prevItems;
383
+ });
384
+ };
385
+
386
+ const handleClick = (value) => {
387
+ if (showFR === value) {
388
+ setShowFR('')
389
+ } else {
390
+ setShowFR(value);
391
+ }
392
+ };
393
+
394
+ const getDataFilter = async () => {
395
+ if (props.api) {
396
+ setLoading(true)
397
+ try {
398
+ const response = await fetch(queryURL, {
399
+ method: 'GET',
400
+ headers: authenticated_header()
401
+ });
402
+
403
+ if (!response.ok) {
404
+ throw new Error(`HTTP ERROR! STATUS: ${response.status}`);
405
+ }
406
+
407
+ const data = await response.json();
408
+ setReponseDetail(data)
409
+ setDataFilters(data.results)
410
+ setLoading(false)
411
+ } catch (error) {
412
+ console.error('ERROR FETCHING DATA', error);
413
+ }
414
+ }
415
+ };
416
+
417
+ const getData = async () => {
418
+ if (props.api) {
419
+ setLoading2(true)
420
+ try {
421
+ const response = await fetch(`${addressIpApi}${props.api}?all=true&center_id=${center?.id ?? null}&date_system=${dateSystem ? DATETIME.formatYYYYMMDD(dateSystem) : `${Date().getFullYear()}-12-31`}&year=${financialYear ?? new Date().getFullYear()}${props.additional_params ?? ''}`, {
422
+ method: 'GET',
423
+ headers: authenticated_header()
424
+ });
425
+
426
+ if (!response.ok) {
427
+ throw new Error('Error fetching data');
428
+ }
429
+
430
+ const data = await response.json();
431
+ setData(data.data)
432
+ const listFilter = []
433
+ for (let idx = 0; idx < headers.length; idx++) {
434
+ if (headers[idx].filterable) {
435
+ listFilter.push(data.data.map(item => ({ id: item.id, name: headers[idx].formule ? headers[idx].formule(item) : item[`${headers[idx].key}`] })))
436
+ }
437
+ }
438
+ setListFiltres(listFilter)
439
+ setLoading2(false)
440
+
441
+ } catch (error) {
442
+ console.error('Error fetching data:', error);
443
+ }
444
+ }
445
+ }
446
+
447
+ const deleteData = async (id) => {
448
+
449
+ const answer = await sweetAlertRef.current.afficherConfirmation(
450
+ 'Voulez-vous supprimer ce prospect?',
451
+ 'Oui',
452
+ 'Annuler'
453
+ );
454
+
455
+ if (answer) {
456
+ try {
457
+ await axios.delete(`${addressIpApi}/crm/lead-genuis/prospects/${id}`, {
458
+ headers: authenticated_header()
459
+ });
460
+
461
+ sweetAlertRef.current.afficherAlerte('success', 'prospect supprimé.');
462
+ getData();
463
+
464
+ } catch (error) {
465
+ sweetAlertRef.current.afficherAlerte('error', 'Une erreur est survenue lors de la suppression');
466
+ console.error("Error saving fund call:", error);
467
+ }
468
+ }
469
+ }
470
+
471
+ const markascomplete = async (item) => {
472
+ const answer = await sweetAlertRef.current.afficherConfirmation(
473
+ 'Voulez-vous achevez la procedure de ce prospect?',
474
+ 'Oui',
475
+ 'Annuler'
476
+ );
477
+
478
+ if (answer) {
479
+ if (item.qualified && !item.completed) {
480
+ fetch(
481
+ `${addressIpApi}/crm/lead-genuis/prospects/${item.id}/markascomplete/`,
482
+ {
483
+ method: "PUT",
484
+ headers: authenticated_header(),
485
+ body: JSON.stringify({
486
+ lead_id: item.id,
487
+ }),
488
+ }
489
+ )
490
+ .then((response) => response.json())
491
+ .then((rep_data) => {
492
+ if (rep_data.success) {
493
+ sweetAlertRef.current.afficherAlerte('success', 'procedure achevée.');
494
+ const dup_data = [...data]
495
+ const itemIndex = dup_data.findIndex((o) => o.id === item.id)
496
+ if (itemIndex >= 0) {
497
+ dup_data[itemIndex].completed = true
498
+ }
499
+ setData(dup_data)
500
+ } else {
501
+ sweetAlertRef.current.afficherAlerte('error', `une erreur est survenue. Veuillez reessayer`);
502
+ }
503
+
504
+
505
+ })
506
+ .catch((error) => {
507
+ console.error("Error:", error);
508
+ });
509
+
510
+ } else {
511
+ sweetAlertRef.current.afficherAlerte('error', `veuillez achevez la qualification`);
512
+ }
513
+ }
514
+ };
515
+
516
+
517
+
518
+ const exportDateToExcelFile = async () => {
519
+ console.log('billing data.xlsx');
520
+
521
+ try {
522
+
523
+ const params = new URLSearchParams({...filters,export:'true'}).toString();
524
+ const QueryURL= `${addressIpApi}${props.api}?${params}&date_system=${dateSystem ? DATETIME.formatYYYYMMDD(dateSystem) : `${Date().getFullYear()}-12-31`}&year=${financialYear ?? new Date().getFullYear()}${props.additional_params ?? ''}`;
525
+ setIsLoadingCancelable(true);
526
+
527
+ // ${params}
528
+ const response = await axios.get(QueryURL,
529
+ {
530
+ headers: authenticated_header(),
531
+
532
+ responseType: 'blob',
533
+ }
534
+ );
535
+ setIsLoadingCancelable(false);
536
+
537
+ const url = window.URL.createObjectURL(new Blob([response.data]));
538
+ const link = document.createElement("a");
539
+ link.href = url;
540
+ link.download = "data.xlsx";
541
+ document.body.appendChild(link);
542
+
543
+ link.click();
544
+
545
+ document.body.removeChild(link);
546
+ window.URL.revokeObjectURL(url);
547
+ } catch (error) {
548
+ console.error('Error downloading the file:', error);
549
+ }
550
+ };
551
+
552
+ const getUniqueValue = (dataz) => {
553
+ const seen = new Set();
554
+ return dataz.filter(data => {
555
+ const duplicate = seen.has(data.name);
556
+ seen.add(data.name);
557
+ return !duplicate;
558
+ });
559
+ };
560
+
561
+ const { onDownload } = useDownloadExcel({
562
+ currentTableRef: tableRefExportExcel.current,
563
+ filename: props.title,
564
+ sheet: "pradium-tech"
565
+ });
566
+
567
+ const getAttachments = async () => {
568
+ try {
569
+ const response = await axios.get(`${addressIpApi}/attachment/attachments/?center_id=${center.id}&global_code=${props.globalCode}`);
570
+ setAttachments(response.data);
571
+ } catch (error) {
572
+ console.error("Error fetching data d:", error.message);
573
+ }
574
+ };
575
+
576
+ const getNextPage = async () => {
577
+ if (props.api) {
578
+ setLoadingIS(true)
579
+ try {
580
+ const response = await fetch(reponseDetail.paginate.next, {
581
+ method: 'GET',
582
+ headers: authenticated_header()
583
+ });
584
+
585
+ if (!response.ok) {
586
+ throw new Error(`HTTP ERROR! STATUS: ${response.status}`);
587
+ }
588
+
589
+ const data = await response.json();
590
+ setReponseDetail(data)
591
+ const newData = dataFilters.concat(data.results)
592
+ setDataFilters(newData)
593
+ setLoadingIS(false)
594
+ } catch (error) {
595
+ console.error('ERROR FETCHING DATA', error);
596
+ }
597
+ }
598
+ };
599
+
600
+ const handleScroll = () => {
601
+ const { scrollTop, clientHeight, scrollHeight } =
602
+ document.documentElement;
603
+
604
+ if (scrollTop + clientHeight >= scrollHeight) {
605
+ getNextPage()
606
+ }
607
+ };
608
+
609
+ // ---------------------------------------------------------------------------
610
+ // ---------------------------------------------------------------------------
611
+
612
+
613
+
614
+
615
+ // ---------------------------------------------------------------------------
616
+ // ---------------------------------EFFECTS-----------------------------------
617
+ // ---------------------------------------------------------------------------
618
+
619
+ useEffect(() => {
620
+ const handleClickOutside = (event) => {
621
+ if (!event.target.classList.contains("selected")) {
622
+ setLineSelected('');
623
+ }
624
+ };
625
+
626
+ document.addEventListener('mousedown', handleClickOutside);
627
+
628
+ return () => {
629
+ document.removeEventListener('mousedown', handleClickOutside);
630
+ };
631
+ }, []);
632
+
633
+ useEffect(() => {
634
+ const params = new URLSearchParams(filters).toString();
635
+ setQueryURL(`${addressIpApi}${props.api}?${params}&date_system=${dateSystem ? DATETIME.formatYYYYMMDD(dateSystem) : `${Date().getFullYear()}-12-31`}&year=${financialYear ?? new Date().getFullYear()}${props.additional_params ?? ''}`);
636
+ setFrontQueryURL(`${params}&date_system=${dateSystem ? DATETIME.formatYYYYMMDD(dateSystem) : `${Date().getFullYear()}-12-31`}&year=${financialYear ?? new Date().getFullYear()}${props.additional_params ?? ''}`);
637
+ const urlWithoutParams = location.pathname;
638
+ navigate(`${urlWithoutParams}?${params}`)
639
+ }, [filters]);
640
+
641
+ useEffect(() => {
642
+ getData();
643
+ }, [center, rerender]);
644
+
645
+ useEffect(() => {
646
+ getDataFilter();
647
+ }, [center, queryURL, rerender]);
648
+
649
+ useEffect(() => {
650
+ const columns = []
651
+ headers.forEach(item1 => item1.filterable && columns.push({ label: item1.name, type: item1.type, key: item1.search_name ? item1.search_name : item1.key }))
652
+ setFiltres(columns)
653
+ props.btn_add !== undefined && setBtnAdd(props.btn_add)
654
+ }, [])
655
+
656
+ useEffect(() => {
657
+ props.hasAttach && getAttachments();
658
+ }, []);
659
+
660
+ useEffect(() => {
661
+ if (infinite_scroll) {
662
+ window.addEventListener("scroll", handleScroll);
663
+
664
+ return () => window.removeEventListener("scroll", handleScroll);
665
+ }
666
+ }, []);
667
+
668
+ const memoizedAttachments = useMemo(() => {
669
+ if (attachments.length > 0) {
670
+ return <Attachments reload={props.reloadAttachment ?? null} data={attachments} global_code={props.globalCode} />;
671
+ }
672
+ return null;
673
+ }, [attachments, props.globalCode, props.reloadAttachment]);
674
+
675
+
676
+
677
+
678
+ // ---------------------------------------------------------------------------
679
+ // ---------------------------------------------------------------------------
680
+
681
+
682
+ return (
683
+ <>
684
+ <section
685
+ id={props.id}
686
+ className={`fdrawer-container flex-col ${props.inWorkspace && "in-workspace"} ${props.wTotalCard && "w-total-card"} ${props.wMenu && "w-menu"}`}
687
+ style={{
688
+ height: props.height && ""
689
+ }}
690
+ >
691
+ <div className="wise-main-content main fcustom-main d-flex flex-row col">
692
+ <div className='wise-body card-global-green h-100 col flex-col'>
693
+ <div className={`card-header change-background-color-header-gray ${props.noTitle ? "d-none" : "flex-between"}`}>
694
+ <h4 className='title general-title mb-0'>
695
+ {props.title}
696
+ </h4>
697
+
698
+ <div className="flex-row gap-4 align-center">
699
+ {props.sideTitle}
700
+
701
+ {props.wReturn && <IoCloseCircleSharp className='icon rem-15' role='button' onClick={() => navigate(props.wReturn)} />}
702
+ </div>
703
+ </div>
704
+ <div className={`wise-card card-body gap-2 flex-col`}>
705
+
706
+
707
+ <div className='h-100 d-flex flex-column overflow-hidden'>
708
+ <div className='row flex-grow-1 flex-shrink-1 flex-basis-0 d-flex drawer-main-content'>
709
+ {
710
+ type !== "any" ? (
711
+ <>
712
+ <div className={`col`}>
713
+
714
+ <div className='table-custom custom-list-journal-body-table'>
715
+ <div className={`table-responsive type-table ${props.inWorkspace && "in-workspace"} ${props.wTotalCard && "w-total-card"} ${props.wMenu && "w-menu"}`}>
716
+ {
717
+ table_type === "collapse-simple" && (
718
+ <TableCollapseSimple
719
+ arch={props.arch}
720
+ secondElements={props.secondElements}
721
+ triggerActions={props.triggerActions}
722
+ actions={props.actions}
723
+ dataFilters={dataFilters}
724
+ loading={loading}
725
+ loading2={loading2}
726
+ maxText={maxText}
727
+ data={data}
728
+ headers={headers}
729
+ totalHeaders={totalHeaders}
730
+ totalMainHeaders={totalMainHeaders}
731
+ filters={filters}
732
+ setFilters={setFilters}
733
+ setComment={setComment}
734
+ setSecondInfoComment={setSecondInfoComment}
735
+ setShowModalComment={setShowModalComment}
736
+ showContent={showContent}
737
+ setShowContent={setShowContent}
738
+ lineSelected={lineSelected}
739
+ setLineSelected={setLineSelected}
740
+ isSorting={isSorting}
741
+ setIsSorting={setIsSorting}
742
+ />
743
+ )
744
+ }
745
+ {
746
+ table_type === "with-checkbox" && (
747
+ <TableWithCheckbox
748
+ selected={selected}
749
+ actions={props.actions}
750
+ dataFilters={dataFilters}
751
+ loading={loading}
752
+ maxText={maxText}
753
+ data={data}
754
+ headers={headers}
755
+ totalHeaders={totalHeaders}
756
+ filters={filters}
757
+ setFilters={setFilters}
758
+ setComment={setComment}
759
+ setSecondInfoComment={setSecondInfoComment}
760
+ setShowModalComment={setShowModalComment}
761
+ lineSelected={lineSelected}
762
+ setLineSelected={setLineSelected}
763
+ isSorting={isSorting}
764
+ setIsSorting={setIsSorting}
765
+ />
766
+ )
767
+ }
768
+ {
769
+ table_type === "basic" && (
770
+ <TableBasic
771
+ dataFilters={dataFilters}
772
+ actions={props.actions}
773
+ loading={loading}
774
+ maxText={maxText}
775
+ data={data}
776
+ headers={headers}
777
+ totalHeaders={totalHeaders}
778
+ filters={filters}
779
+ setFilters={setFilters}
780
+ setComment={setComment}
781
+ setSecondInfoComment={setSecondInfoComment}
782
+ setShowModalComment={setShowModalComment}
783
+ lineSelected={lineSelected}
784
+ setLineSelected={setLineSelected}
785
+ isSorting={isSorting}
786
+ setIsSorting={setIsSorting}
787
+ />
788
+ )
789
+ }
790
+ </div>
791
+ </div>
792
+
793
+ </div>
794
+
795
+ <div className={`w-100 ${props.noPagination ? "d-none" : "flex-between align-end"}`}>
796
+ {
797
+ dataFilters.length > 0 ? <FilterPagination filters={[filters, setFilters]} reponseDetail={reponseDetail} setQueryURL={setQueryURL} frontqueryURL={frontqueryURL} /> : <div className=""></div>
798
+ }
799
+
800
+ {
801
+ props.summary ? <FooterSummary colonnes={props.summary.colonnes} rows={props.summary.rows} param={data} /> : <div className=""></div>
802
+ }
803
+ </div>
804
+ </>
805
+ ) : (
806
+ <>
807
+ <div className={` ${!showAttachments ? " d-flex flex-col w-100 flex-fill" : "d-none"}`}>
808
+ {props.children}
809
+ </div>
810
+ <div className={`${showAttachments ? " d-flex flex-col w-100 flex-fill" : "d-none"}`}>
811
+ {memoizedAttachments}
812
+ </div>
813
+
814
+
815
+
816
+
817
+ </>
818
+
819
+ )
820
+ }
821
+
822
+
823
+ </div>
824
+
825
+
826
+ </div>
827
+
828
+
829
+
830
+ </div>
831
+ </div>
832
+
833
+ {showFR === "f" && (
834
+ <>
835
+ <div className={`param-bar flex-col gap-2 ${showFR === "f" ? "showFiltres" : "hideFiltres"}`}>
836
+ <div className='card-global-green col flex-col'>
837
+ <div className={`card-header change-background-color-header-gray`}>
838
+ <h4 className='title general-title mb-0'>
839
+ <img className='icon spe-img2' src={activeBouttonDrawerFilter} alt="icon" />
840
+ {DICTIONNARY.Filtres[language]}
841
+ </h4>
842
+ </div>
843
+ {
844
+ loading2 ? (
845
+ <div className='bg-white radius-1 flex-center col'>
846
+ <div className="text-center">...chargement</div>
847
+ </div>
848
+ ) : data.length > 0 && filtres.length > 0 ? (
849
+ <div className='card-body col p-2'>
850
+ <div className="w-100 justify-end">
851
+ <NewButton
852
+ type="custom"
853
+ icon={<IoMdRefresh className='icon rem-15' />}
854
+ title={DICTIONNARY.ViderTousLesChamps[language]}
855
+ onclick={() => {
856
+ champsRef.current.forEach(item => {
857
+ if (item) {
858
+ item.triggerClick()
859
+ }
860
+ })
861
+ champsRef2.current.forEach(item => {
862
+ if (item) {
863
+ item.value = ""
864
+ }
865
+ })
866
+ setFilters(filtersMemo)
867
+ }}
868
+ />
869
+ </div>
870
+ <div className={`w-100`}>
871
+ {
872
+ filtres.map((item, idx) => (
873
+ <div className="fcontent" key={item.key}>
874
+ {
875
+ item.type === "list" ? (
876
+ <div className="ligne-input mb-1" key={item.key}>
877
+ <label htmlFor="">
878
+ {item.label}
879
+ </label>
880
+ <CustomSelector
881
+ ref={(el) => (champsRef.current[idx] = el)}
882
+ optionsInitial={
883
+ item.filter_options
884
+ ? item.filter_options.map(item2 => ({
885
+ id: item2.value,
886
+ value: item2.value,
887
+ label: item2.label
888
+ }))
889
+ : getUniqueValue(listFiltres[idx]).map(item2 => ({
890
+ id: item2.name,
891
+ value: item2.name,
892
+ label: item2.name
893
+ }))
894
+ }
895
+ onSelect={(selectedValue) => {
896
+ const newCle = `${item.key}__exact`
897
+ const copy = {
898
+ ...filters,
899
+ search_type: 'filter',
900
+ }
901
+ copy[`${newCle}`] = selectedValue
902
+ setFilters(copy)
903
+ }}
904
+ placeholder={DICTIONNARY.Select[language]}
905
+ loading={null}
906
+ />
907
+ {/* <select name="" id="">
908
+ {props.lists && props.lists[item.label].map((option, opt_index)=> (
909
+ <option key={`2543Trr${option}ezr${item.label}`} value={opt_index}>{option}</option>
910
+ ))}
911
+ </select> */}
912
+ </div>
913
+ ) : item.type === "amount" ? (
914
+ <div className="ligne-input mb-1" key={item.key}>
915
+ <label htmlFor="">{item.label}</label>
916
+ <div className="col d-flex flex-row">
917
+ <input type="number" className='col percent-input' placeholder='min'
918
+ ref={(el) => (champsRef2.current[idx] = el)}
919
+ onChange={(e) => {
920
+ const newCle = `${item.key}__gte`
921
+ const copy = {
922
+ ...filters,
923
+ search_type: 'filter',
924
+ }
925
+ copy[`${newCle}`] = e.target.value
926
+ setFilters(copy)
927
+ }} />
928
+ <div className="mx-1">-</div>
929
+ <input type="number" className='col percent-input' placeholder='max'
930
+ ref={(el) => (champsRef2.current[idx] = el)}
931
+ onChange={(e) => {
932
+ const newCle = `${item.key}__lte`
933
+ const copy = {
934
+ ...filters,
935
+ search_type: 'filter',
936
+ }
937
+ copy[`${newCle}`] = e.target.value
938
+ setFilters(copy)
939
+ }} />
940
+ </div>
941
+ </div>
942
+ ) : item.type === "percent" ? (
943
+ <div className="ligne-input mb-1" key={item.key}>
944
+ <label htmlFor="">{item.label}</label>
945
+ <div className="col d-flex flex-row">
946
+ <input type="number" className='col percent-input' placeholder='min' />
947
+ <div className="mx-1">-</div>
948
+ <input type="number" className='col percent-input' placeholder='max' />
949
+ </div>
950
+ </div>
951
+ ) : item.type === "date" ? (
952
+ <div className="ligne-input mb-1" key={item.key}>
953
+ <label htmlFor="">{item.label}</label>
954
+ <div className="col d-flex flex-row">
955
+ <input type="date" className='col'
956
+ ref={(el) => (champsRef2.current[idx] = el)}
957
+ onChange={(e) => {
958
+ const newCle = `${item.key}__gte`
959
+ const copy = {
960
+ ...filters,
961
+ search_type: 'filter',
962
+ }
963
+ copy[`${newCle}`] = e.target.value
964
+ setFilters(copy)
965
+ }}
966
+ />
967
+ <div className="mx-1">-</div>
968
+ <input type="date" className='col'
969
+ ref={(el) => (champsRef2.current[idx] = el)}
970
+ onChange={(e) => {
971
+ const newCle = `${item.key}__lte`
972
+ const copy = {
973
+ ...filters,
974
+ search_type: 'filter',
975
+ }
976
+ copy[`${newCle}`] = e.target.value
977
+ setFilters(copy)
978
+ }} />
979
+ </div>
980
+ </div>
981
+ ) : item.type === "number" ? (
982
+ <div className="ligne-input mb-1" key={item.key}>
983
+ <label htmlFor="">{item.label}</label>
984
+ <div className="col d-flex flex-row">
985
+ <input type="number" className='col percent-input' placeholder='min'
986
+ ref={(el) => (champsRef2.current[idx] = el)}
987
+ onChange={(e) => {
988
+ const newCle = `${item.key}__gte`
989
+ const copy = {
990
+ ...filters,
991
+ search_type: 'filter',
992
+ }
993
+ copy[`${newCle}`] = e.target.value
994
+ setFilters(copy)
995
+ }} />
996
+ <div className="mx-1">-</div>
997
+ <input type="number" className='col percent-input' placeholder='max'
998
+ ref={(el) => (champsRef2.current[idx] = el)}
999
+ onChange={(e) => {
1000
+ const newCle = `${item.key}__lte`
1001
+ const copy = {
1002
+ ...filters,
1003
+ search_type: 'filter',
1004
+ }
1005
+ copy[`${newCle}`] = e.target.value
1006
+ setFilters(copy)
1007
+ }} />
1008
+ </div>
1009
+ </div>
1010
+ ) : item.type === "checkbox" ? (
1011
+ <div className="ligne-input mb-1" key={item.key}>
1012
+ <input type="checkbox" className='col' />
1013
+ <label htmlFor="">{item.label}</label>
1014
+ </div>
1015
+ ) : (
1016
+ <div className="ligne-input mb-1" key={item.key}>
1017
+ <label htmlFor="">{item.label}</label>
1018
+ <CustomSelector
1019
+ ref={(el) => (champsRef.current[idx] = el)}
1020
+ defaultSelectedValue={csv}
1021
+ optionsInitial={getUniqueValue(listFiltres[idx]).map(item2 => ({
1022
+ id: item2.key,
1023
+ value: item2.name,
1024
+ label: item2.name
1025
+ }))}
1026
+ onSelect={(selectedValue) => {
1027
+ const newCle = `${item.key}__exact`
1028
+ const copy = {
1029
+ ...filters,
1030
+ search_type: 'filter',
1031
+ }
1032
+ copy[`${newCle}`] = selectedValue
1033
+ setFilters(copy)
1034
+ }}
1035
+ placeholder={DICTIONNARY.Select[language]}
1036
+ loading={null}
1037
+ />
1038
+ </div>
1039
+ )
1040
+ }
1041
+ </div>
1042
+ ))
1043
+ }
1044
+ </div>
1045
+ </div>
1046
+ ) : (
1047
+ <div className='bg-white radius-1 flex-center col flex-col gap-3'>
1048
+ <div className="icon-div"><FaFilterCircleXmark className='icon rem-5' /></div>
1049
+ <div className="text-center fw-bold">{DICTIONNARY.NoDataToFilter[language]}</div>
1050
+ </div>
1051
+ )
1052
+ }
1053
+ </div>
1054
+ </div>
1055
+ </>
1056
+ )}
1057
+ {showFR === "fm" && (
1058
+ <>
1059
+ <div className={`param-bar flex-col gap-2 ${showFR === "f" ? "showFiltres" : "hideFiltres"}`}>
1060
+ <div className='card-global-green col flex-col'>
1061
+ <div className={`card-header change-background-color-header-gray`}>
1062
+ <h4 className='title general-title mb-0'>
1063
+ <img className='icon spe-img2' src={activeBouttonDrawerFilter} alt="icon" />
1064
+ {DICTIONNARY.Filtres[language]}
1065
+ </h4>
1066
+ </div>
1067
+ {
1068
+ loading2 ? (
1069
+ <div className='bg-white radius-1 flex-center col'>
1070
+ <div className="text-center">...chargement</div>
1071
+ </div>
1072
+ ) : data.length > 0 && filtres.length > 0 ? (
1073
+ <div className='card-body col p-2'>
1074
+ <div className="w-100 justify-end">
1075
+ <NewButton
1076
+ type="custom"
1077
+ icon={<IoMdRefresh className='icon rem-15' />}
1078
+ title={DICTIONNARY.ViderTousLesChamps[language]}
1079
+ onclick={() => {
1080
+ champsRef.current.forEach(item => {
1081
+ if (item) {
1082
+ item.triggerClick()
1083
+ }
1084
+ })
1085
+ champsRef2.current.forEach(item => {
1086
+ if (item) {
1087
+ item.value = ""
1088
+ }
1089
+ })
1090
+ setFilters(filtersMemo)
1091
+ }}
1092
+ />
1093
+ </div>
1094
+ <div className='w-100'>
1095
+
1096
+ </div>
1097
+
1098
+ </div>
1099
+ ) : (
1100
+ <div className='bg-white radius-1 flex-center col flex-col gap-3'>
1101
+ <div className="icon-div"><FaFilterCircleXmark className='icon rem-5' /></div>
1102
+ <div className="text-center fw-bold">{DICTIONNARY.NoDataToFilter[language]}</div>
1103
+ </div>
1104
+ )
1105
+ }
1106
+ </div>
1107
+ </div>
1108
+ </>
1109
+ )}
1110
+
1111
+ {showFR === "r" && (
1112
+ <>
1113
+ <div className={`param-bar flex-col gap-2 ${showFR === "r" ? "showRecherche" : "hideRecherche"}`}>
1114
+ <div className='card-global-green col flex-col'>
1115
+ <div className={`card-header change-background-color-header-gray`}>
1116
+ <h4 className='title general-title mb-0'>
1117
+ <img className='icon spe-img2' src={activeBouttonDrawerSearch} alt="icon" />
1118
+ {DICTIONNARY.Search[language]}
1119
+ </h4>
1120
+ </div>
1121
+
1122
+ <div className='card-body col p-2'>
1123
+ <div className="w-100 justify-end">
1124
+ <NewButton
1125
+ type="custom"
1126
+ icon={<IoMdRefresh className='icon rem-15' />}
1127
+ onclick={() => {
1128
+ setSelectedItems([])
1129
+ setSearch("")
1130
+ const updatedFilters = { center_id: filters.center_id, search_type: 'search' };
1131
+ if (search !== "") {
1132
+ selectedItems.forEach((item) => {
1133
+
1134
+ updatedFilters[`${item}__icontains`] = "";
1135
+ })
1136
+ // for (const key in filters) {
1137
+ // if (filters.hasOwnProperty(key) && key !== 'center_id' && key !== 'page') {
1138
+ // updatedFilters[key] = e.target.value;
1139
+ // }
1140
+ // }
1141
+
1142
+ setFilters(updatedFilters);
1143
+ }
1144
+ searchSelect.current.value = ""
1145
+ }}
1146
+ title={DICTIONNARY.ViderTousLesChamps[language]}
1147
+ />
1148
+ </div>
1149
+ <div className={`w-100 fdrawer-search`}>
1150
+ <div className='grid-custom-one-column'>
1151
+ <label htmlFor="" className="form-label">{DICTIONNARY.SelectColumns[language]}</label>
1152
+ <select className="form-select" onChange={handleSelectedItem} name='itemSelect' ref={searchSelect}>
1153
+ <option selected="">{DICTIONNARY.Select[language]}</option>
1154
+ {
1155
+ headers.map((item, index) => (
1156
+ <option key={index} value={item.search_name}>{item.name}</option>
1157
+ ))
1158
+ }
1159
+ </select>
1160
+ </div>
1161
+ <div className='item-selected-searched'>
1162
+ {
1163
+ selectedItems.length === 0 ? (
1164
+ <></>
1165
+ ) : (
1166
+ <>
1167
+ <div className='item-selected'>
1168
+ {
1169
+ selectedItems.map((item, index) => (
1170
+ <>
1171
+ <span title='Cliquer pour supprimer' onClick={() => handleRemoveItem(item)} >{headers.find(hi => hi.key === item)?.name ?? ''}</span>
1172
+ </>
1173
+ ))
1174
+ }
1175
+ </div>
1176
+ <div className=''>
1177
+ <label htmlFor="" className="form-label" >Recherche</label>
1178
+ <input className="form-control" type="search" value={search}
1179
+ onChange={(e) => {
1180
+ setSearch(e.target.value)
1181
+ const updatedFilters = { center_id: filters.center_id, search_type: 'search' };
1182
+ selectedItems.forEach((item) => {
1183
+
1184
+ updatedFilters[`${item}__icontains`] = e.target.value;
1185
+ })
1186
+ // for (const key in filters) {
1187
+ // if (filters.hasOwnProperty(key) && key !== 'center_id' && key !== 'page') {
1188
+ // updatedFilters[key] = e.target.value;
1189
+ // }
1190
+ // }
1191
+
1192
+ setFilters(updatedFilters);
1193
+
1194
+ }} />
1195
+ </div>
1196
+ {/* <div className='btn-search'>
1197
+ <img src={bouttonDrawerValidateSearch} className='btn-icon' />
1198
+ </div> */}
1199
+ </>
1200
+ )
1201
+ }
1202
+ </div>
1203
+ </div>
1204
+ </div>
1205
+ </div>
1206
+ </div>
1207
+ </>
1208
+ )}
1209
+
1210
+ {/* {showFR && !["r", "f"].includes(showFR) && (
1211
+ <>
1212
+ <div className={`param-bar flex-col gap-2 ${showFR === "r" ? "showRecherche" : "hideRecherche"}`}>
1213
+ <div className='card-global-green col flex-col'>
1214
+ <div className={`card-header change-background-color-header-gray`}>
1215
+ <h4 className='title general-title mb-0'>
1216
+ <img className='icon spe-img2' src={activeBouttonDrawerSearch} alt="icon" />
1217
+ {DICTIONNARY.Search[language]}
1218
+ </h4>
1219
+ </div>
1220
+
1221
+ <div className='card-body col p-2'>
1222
+ </div>
1223
+ </div>
1224
+ </div>
1225
+ </>
1226
+ )} */}
1227
+
1228
+ {/* ---------------------------------DRAWER BUTTONS----------------------------------- */}
1229
+ <div className={`fdrawer-btns-container ${props.noTitle && "mt-0"}`}>
1230
+ <div className="btnz-container" ref={btnsContainer}>
1231
+ <div className="fdrawer-btn">
1232
+ {showFR ? <RiMenuUnfoldLine /> : <RiMenuFoldLine />}
1233
+ </div>
1234
+ {
1235
+ !noFilter && (
1236
+ <div className={`fdrawer-btn ${showFR === "f" && "active"}`} onClick={() => {
1237
+
1238
+ if (type === 'any') {
1239
+ setShowPeriodModal(true)
1240
+ } else {
1241
+ handleClick("f")
1242
+ }
1243
+
1244
+ }}>
1245
+ <img className='no-hover' src={bouttonDrawerConsult} alt="icon" />
1246
+ <img className='for-hover' src={activebouttonDrawerConsult} alt="icon" />
1247
+ <div className="label-container">
1248
+ {DICTIONNARY.Filtres[language]}
1249
+ </div>
1250
+ </div>
1251
+ )
1252
+ }
1253
+ {
1254
+ !noSearch && (
1255
+ <div className={`fdrawer-btn ${showFR === "r" && "active"} ${type === "any" && "d-none"}`} onClick={() => handleClick("r")}>
1256
+ <img className='no-hover' src={bouttonDrawerSearch} alt="icon" />
1257
+ <img className='for-hover' src={activeBouttonDrawerSearch} alt="icon" />
1258
+ <div className="label-container">
1259
+ {DICTIONNARY.Search[language]}
1260
+ </div>
1261
+ </div>
1262
+ )
1263
+ }
1264
+ {
1265
+ canExport && (
1266
+ <div className={`fdrawer-btn ${showFR === "r" && "active"} ${type === "any" && "d-none"}`} onClick={() => exportDateToExcelFile()}>
1267
+
1268
+ <img className='no-hover' src={bouttonDrawerexcelExport} alt="icon" />
1269
+ <img className='for-hover' src={activebouttonDrawerExcelExport} alt="icon" />
1270
+ <div className="label-container">
1271
+ {DICTIONNARY.ExportExcel[language]}
1272
+ </div>
1273
+ </div>
1274
+ )
1275
+ }
1276
+ {
1277
+ btnsList.length > 0 && btnsList.map(item => (
1278
+ <>
1279
+ <div className={`fdrawer-btn ${item.dropdown && "dropstart"}`} role='button' onClick={item.onclick ?? (() => '')}>
1280
+ {
1281
+ item.type !== "custom" ?
1282
+ (drawerActions[item.type].icon.length > 1 ? (
1283
+ item.dropdown ? (
1284
+ <Link
1285
+ target={item.target ?? "_self"}
1286
+ to={item.to ?? "#"}
1287
+ data-bs-toggle="dropdown"
1288
+ aria-expanded="false"
1289
+ >
1290
+ <img className='no-hover' src={drawerActions[item.type].icon[0]} alt="icon" />
1291
+ <img className='for-hover' src={drawerActions[item.type].icon[1]} alt="icon" />
1292
+ </Link>
1293
+ ) : (
1294
+ <Link
1295
+ target={item.target ?? "_self"}
1296
+ to={item.to ?? "#"}
1297
+ >
1298
+ <img className='no-hover' src={drawerActions[item.type].icon[0]} alt="icon" />
1299
+ <img className='for-hover' src={drawerActions[item.type].icon[1]} alt="icon" />
1300
+ </Link>
1301
+ )
1302
+ ) : (
1303
+ item.dropdown ? (
1304
+ <Link
1305
+ target={item.target ?? "_self"}
1306
+ to={item.to ?? "#"}
1307
+ data-bs-toggle="dropdown"
1308
+ aria-expanded="false"
1309
+ >
1310
+ {drawerActions[item.type].icon[0]}
1311
+ </Link>
1312
+ ) : (
1313
+ <Link
1314
+ target={item.target ?? "_self"}
1315
+ to={item.to ?? "#"}
1316
+ >
1317
+ {drawerActions[item.type].icon[0]}
1318
+ </Link>
1319
+ )
1320
+ )
1321
+ ) : (item.icon.length > 1 ? (
1322
+ item.dropdown ? (
1323
+ <Link
1324
+ target={item.target ?? "_self"}
1325
+ to={item.to ?? "#"}
1326
+ data-bs-toggle="dropdown"
1327
+ aria-expanded="false"
1328
+ >
1329
+ {
1330
+ item.icon_type === "img" ? (
1331
+ <>
1332
+ <img className='no-hover' src={item.icon[0]} alt="icon" />
1333
+ <img className='for-hover' src={item.icon[1]} alt="icon" />
1334
+ </>
1335
+ ) : (
1336
+ <>
1337
+ {item.icon[0]}
1338
+ {item.icon[1]}
1339
+ </>
1340
+ )
1341
+ }
1342
+ </Link>
1343
+ ) : (
1344
+ <Link
1345
+ target={item.target ?? "_self"}
1346
+ to={item.to ?? "#"}
1347
+ >
1348
+ {
1349
+ item.icon_type === "img" ? (
1350
+ <>
1351
+ <img className='no-hover' src={item.icon[0]} alt="icon" />
1352
+ <img className='for-hover' src={item.icon[1]} alt="icon" />
1353
+ </>
1354
+ ) : (
1355
+ <>
1356
+ {item.icon[0]}
1357
+ {item.icon[1]}
1358
+ </>
1359
+ )
1360
+ }
1361
+ </Link>
1362
+ )
1363
+ ) : (
1364
+ item.dropdown ? (
1365
+ <Link
1366
+ target={item.target ?? "_self"}
1367
+ to={item.to ?? "#"}
1368
+ data-bs-toggle="dropdown"
1369
+ aria-expanded="false"
1370
+ >
1371
+ {item.icon[0]}
1372
+ </Link>
1373
+ ) : (
1374
+ <Link
1375
+ target={item.target ?? "_self"}
1376
+ to={item.to ?? "#"}
1377
+ >
1378
+ {item.icon[0]}
1379
+ </Link>
1380
+ )
1381
+ )
1382
+ )
1383
+ }
1384
+ <div className="label-container">
1385
+ {item.label ?? drawerActions[item.type].label}
1386
+ </div>
1387
+ {
1388
+ item.dropdown && (
1389
+ <ul className="dropdown-menu fdrawer-dropdown-menu">
1390
+ {
1391
+ item.dropdown.map(dItem => (
1392
+ <li className="dropdown-item" onClick={dItem.onclick ?? (() => "")}>
1393
+ <Link to={`${dItem.url ?? "#"}`}>
1394
+ {dItem.label}
1395
+ </Link>
1396
+ </li>
1397
+ ))
1398
+ }
1399
+ </ul>
1400
+ )
1401
+ }
1402
+ </div>
1403
+ </>
1404
+ ))
1405
+ }
1406
+ {
1407
+ props.hasImportExcel &&
1408
+ <>
1409
+ <div className="fdrawer-btn" role='button' onClick={() => setShowImportExcel(true)}>
1410
+ <img className='no-hover' src={bouttonDrawerexcelImport} alt="icon" />
1411
+ <img className='for-hover' src={activebouttonDrawerExcelImport} alt="icon" />
1412
+ <div className="label-container">
1413
+ {DICTIONNARY.ImportExcel[language]}
1414
+ </div>
1415
+ </div>
1416
+ </>
1417
+ }
1418
+ {
1419
+ props.hasExportExcel &&
1420
+ <>
1421
+ <div className="fdrawer-btn" role='button' onClick={() => exportDateToExcelFile()}>
1422
+ <img className='no-hover' src={bouttonDrawerexcelExport} alt="icon" />
1423
+ <img className='for-hover' src={activebouttonDrawerExcelExport} alt="icon" />
1424
+ <div className="label-container">
1425
+ {DICTIONNARY.ExportExcel[language]}
1426
+ </div>
1427
+ </div>
1428
+ </>
1429
+ }
1430
+ {
1431
+ props.hasComHis &&
1432
+ <>
1433
+ <div className="fdrawer-btn" role='button' onClick={() => setShowCommunication(true)}>
1434
+ <img className='no-hover' src={bouttonDrawerMessagerie} alt="icon" />
1435
+ <img className='for-hover' src={activebouttonDrawerMessagerie} alt="icon" />
1436
+ <div className="label-container">
1437
+ {DICTIONNARY.CommunicationHistory[language]}
1438
+ </div>
1439
+ </div>
1440
+ </>
1441
+ }
1442
+ {
1443
+ props.hasAttach &&
1444
+ <>
1445
+ <div className="fdrawer-btn" role='button' onClick={() => setShowAttachments(!showAttachments)}>
1446
+ <img className='no-hover' src={bouttonDrawerAttachement} alt="icon" />
1447
+ <img className='for-hover' src={activebouttonDrawerAttachement} alt="icon" />
1448
+ <div className="label-container">
1449
+ {DICTIONNARY.Attachments[language]}
1450
+ </div>
1451
+ </div>
1452
+ </>
1453
+ }
1454
+ {
1455
+ props.hasExport &&
1456
+ <>
1457
+ <div className="fdrawer-btn" role='button' onClick={() => setShowAttachments(!showAttachments)}>
1458
+ <img className='no-hover' src={bouttonDrawerAttachement} alt="icon" />
1459
+ <img className='for-hover' src={activebouttonDrawerAttachement} alt="icon" />
1460
+ <div className="label-container">
1461
+ {DICTIONNARY.Attachments[language]}
1462
+ </div>
1463
+ </div>
1464
+ </>
1465
+ }
1466
+ {
1467
+ props.save &&
1468
+ <>
1469
+ <div
1470
+ className="fdrawer-btn" role='button'
1471
+ onClick={props.save}
1472
+ >
1473
+ <img className='no-hover' src={bouttonDrawerEnregistrer} alt="icon" />
1474
+ <img className='for-hover' src={activebouttonDrawerEnregistrer} alt="icon" />
1475
+ <div className="label-container">
1476
+ {DICTIONNARY.Save[language]}
1477
+ </div>
1478
+ </div>
1479
+ </>
1480
+ }
1481
+ {
1482
+ props.view &&
1483
+ <>
1484
+ <div
1485
+ className="fdrawer-btn"
1486
+ role='button'
1487
+ data-bs-toggle="dropdown"
1488
+ aria-expanded="false"
1489
+ >
1490
+ <FaEye className='no-hover icon white' alt="icon" />
1491
+ <FaEye className='for-hover icon gray' alt="icon" />
1492
+ <div className="label-container">
1493
+ {DICTIONNARY.ChangerLaVue[language]}
1494
+ </div>
1495
+ </div>
1496
+ <ul className="dropdown-menu fdrawer-dropdown-menu">
1497
+ {
1498
+ props.view_actions && props.view_actions.map(item => (
1499
+ <li className="dropdown-item" onClick={item.onclick ?? (() => "")}>
1500
+ {item.label}
1501
+ </li>
1502
+ ))
1503
+ }
1504
+ </ul>
1505
+ </>
1506
+ }
1507
+ </div>
1508
+ <div className="no-floating-btn for-style"></div>
1509
+ <div className="no-floating-btn for-style2"></div>
1510
+ </div>
1511
+ {/* ------------------------------------------------------------------------------------- */}
1512
+ </div>
1513
+ </section>
1514
+ {/* ---------------------------------MODAL COM HIS----------------------------------- */}
1515
+ <FModal
1516
+ type="basic"
1517
+ wHeader={false}
1518
+ show={showCommunication}
1519
+ wBtns={false}
1520
+ modalClassName={"modal-format-attach"}
1521
+ hide={() => setShowCommunication(false)}
1522
+ title={DICTIONNARY.CommunicationHistory[language]}
1523
+ children={
1524
+ <>
1525
+ <CommunicationHistoryComponent lead={props.lead} update_func={props.update_func} />
1526
+ </>
1527
+ }
1528
+ />
1529
+ {/* ------------------------------------------------------------------------------------- */}
1530
+ {/* ---------------------------------MODAL ATTACHMENT------------------------------------ */}
1531
+ {/* <FModal
1532
+ type="basic"
1533
+ key="FM-att-modal"
1534
+ wBtns={false}
1535
+ show={showAttachments}
1536
+ modalClassName={"modal-format-attach"}
1537
+ hide={() => setShowAttachments(false)}
1538
+ title={DICTIONNARY.Attachments[language]}
1539
+ children={memoizedAttachments}
1540
+ /> */}
1541
+ {/* ------------------------------------------------------------------------------------- */}
1542
+ {/* ---------------------------------MODAL IMPORT EXCEL---------------------------------- */}
1543
+ <ExcelImportModal
1544
+ show={showImportExcel}
1545
+ hide={() => setShowImportExcel(false)}
1546
+ api={props.api_import}
1547
+ onSuccess={() => {
1548
+ setShowImportExcel(false);
1549
+ getDataFilter(); // Recharger les données après import
1550
+ }}
1551
+ />
1552
+ {/* ------------------------------------------------------------------------------------- */}
1553
+ {/* ---------------------------------MODAL COMMENTAIRE----------------------------------- */}
1554
+ <FModal
1555
+ type="basic"
1556
+ show={showModalComment}
1557
+ hide={() => setShowModalComment(false)}
1558
+ title={DICTIONNARY.Commentaire[language]}
1559
+ modalClassName={"modal-format-com"}
1560
+ secondInfo={secondInfoComment}
1561
+ wBtns={false}
1562
+ children={
1563
+ <>
1564
+ <div className="col radius-1 p-2 bordered">
1565
+ {comment}
1566
+ </div>
1567
+ </>
1568
+ }
1569
+ />
1570
+ {/* ------------------------------------------------------------------------------------- */}
1571
+
1572
+
1573
+ <ModelTemplapleSimpleV1
1574
+ className={`content`}
1575
+ modalClassName={`crm-modal-custom-drawer`}
1576
+ show={showPeriodModal} hide={() => setShowPeriodModal(false)}
1577
+ children={
1578
+ <>
1579
+ <div className='card-global-green card'>
1580
+ <div className={`card-header change-background-color-header-gray`}>
1581
+ <h4 className='title general-title mb-0'>selectectionner la periode </h4>
1582
+ </div>
1583
+ <div className='content-card'>
1584
+ <CustomFieldset title={`Filtre`} className={`fieldset-drawer-content`} children={
1585
+ <>
1586
+ <div className='grid-custom-one-column content-drawer'>
1587
+ <div className="mb-2">
1588
+ <label htmlFor="" className="form-label">Period</label>
1589
+ <div className="grid-custom-two-column grid-gap-custom">
1590
+ <div className="grid-custom-one-column">
1591
+ <input type="date" value={dateRange.startDate} onChange={changeRange} name="startDate" />
1592
+ </div>
1593
+ <div className="grid-custom-one-column">
1594
+ <input type="date" value={dateRange.endDate} onChange={changeRange} name="endDate" />
1595
+ </div>
1596
+ </div>
1597
+ </div>
1598
+ </div>
1599
+
1600
+
1601
+ </>
1602
+ } />
1603
+ </div>
1604
+ </div>
1605
+ </>
1606
+ }
1607
+ />
1608
+
1609
+ </>
1610
+ );
1611
+ };
1612
+
1613
+
1614
+ export const FooterSummary: React.FC<FooterSummaryProps> = (props) => {
1615
+ return (
1616
+ <>
1617
+ <div className='card-total two'>
1618
+ <div className="">
1619
+ <table className="fsummary-table">
1620
+ <thead>
1621
+ <tr>
1622
+ <th></th>
1623
+ {
1624
+ props.colonnes.map((item, index) => (
1625
+ <th key={`${index}57435674GFHJKB659764`} className='text-center px-1'>{item}</th>
1626
+ ))
1627
+ }
1628
+ </tr>
1629
+ </thead>
1630
+
1631
+ <tbody>
1632
+ {
1633
+ props.rows.map((item, index) => (
1634
+ <tr key={`${index}34843Y6F45FUYG756FJ7UHY65HYGER`} className=''>
1635
+ <td className='pe-1'>{item.label}</td>
1636
+ {
1637
+ item.contents.map((content, index) => (
1638
+ <td key={`${index}34FG2A3QSR4R`} className='text-center'>{content(props.param)}</td>
1639
+ ))
1640
+ }
1641
+ </tr>
1642
+ ))
1643
+ }
1644
+ </tbody>
1645
+ </table>
1646
+ </div>
1647
+ </div>
1648
+ </>
1649
+ );
1650
+ }
1651
+
1652
+
1653
+
1654
+ export const DropdownActionsMenu: React.FC<DropdownActionsMenuProps> = ({ actions, row, setLineSelected, center }) => {
1655
+ return (
1656
+ <ul className="dropdown-menu dropdown-menu-right">
1657
+ {actions?.map((act) => {
1658
+ const link = act.to ? act.to(row) : "";
1659
+ const hasPermission = !act.permission || center?.user_permissions?.includes(act.permission);
1660
+ const handleClick = () => {
1661
+ if (!hasPermission) return;
1662
+ setLineSelected("");
1663
+ act.onclick?.(row);
1664
+ };
1665
+
1666
+ if (act.condition && !act.condition(row)) return null;
1667
+
1668
+ return (
1669
+ <li key={act.label}>
1670
+ {act.onclick ? (
1671
+ <span
1672
+ className={`dropdown-item ${!hasPermission ? 'wise-disabled text-muted' : ''}`}
1673
+ onClick={handleClick}
1674
+ title={!hasPermission ? "Vous n'avez pas l'autorisation requise pour cette action." : ""}
1675
+ style={{ cursor: hasPermission ? "pointer" : "not-allowed" }}
1676
+ >
1677
+ {act.label}
1678
+ </span>
1679
+ ) : (
1680
+ <Link
1681
+ className={`dropdown-item ${!hasPermission ? 'wise-disabled text-muted' : ''}`}
1682
+ to={hasPermission ? link : "#"}
1683
+ title={!hasPermission ? "Vous n'avez pas l'autorisation requise pour cette action." : ""}
1684
+ onClick={(e) => {
1685
+ if (!hasPermission) e.preventDefault();
1686
+ else setLineSelected("");
1687
+ }}
1688
+ style={{ cursor: hasPermission ? "pointer" : "not-allowed" }}
1689
+ >
1690
+ {act.label}
1691
+ </Link>
1692
+ )}
1693
+ </li>
1694
+ );
1695
+ })}
1696
+ </ul>
1697
+ );
1698
+ };
1699
+
1700
+
1701
+
1702
+ export const DynamicTable: React.FC<DynamicTableProps> = (props) => {
1703
+ const [active, setActive] = useState("1");
1704
+ const [rows, setRows] = useState(props.row ?? []);
1705
+ const [rawValues, setRawValues] = useState([])
1706
+ const [newRowValues, setNewRowValues] = useState(props.new_row ?? {})
1707
+
1708
+
1709
+ const handleAddRow = () => {
1710
+ // Générez une nouvelle ligne avec un identifiant unique
1711
+ const newId = Math.random();
1712
+ const newRow = {
1713
+ id: newId,
1714
+ description: '',
1715
+ status: '',
1716
+ };
1717
+
1718
+ setRows([...rows, newRow]);
1719
+ };
1720
+
1721
+ const handleDeleteRow = (inputId) => {
1722
+ // Filtrer les lignes en fonction de l'identifiant unique
1723
+ const updatedRows = rows.filter(row => row.id !== inputId);
1724
+ setRows(updatedRows);
1725
+ };
1726
+
1727
+ const handleDeleteRowValues = (inputId) => {
1728
+ // Filtrer les lignes en fonction de l'identifiant unique
1729
+ const newArr = rawValues.filter(row => row.id !== inputId);
1730
+ setRawValues(newArr);
1731
+ };
1732
+
1733
+ const handleChangeNewRow = (e) => {
1734
+ const copy = newRowValues
1735
+ copy[`${e.currentTarget.name}`] = e.target.value
1736
+ setNewRowValues(copy)
1737
+ }
1738
+
1739
+ const handleResetNewRow = (e) => {
1740
+ const clearRow = {
1741
+ id: Math.random(),
1742
+ description: '',
1743
+ status: '',
1744
+ }
1745
+ setNewRowValues(clearRow)
1746
+ }
1747
+
1748
+ const [selectedItems, setSelectedItems] = useState([]);
1749
+
1750
+ const handleSelectedItem = (event) => {
1751
+ setSelectedItems(prevItems => {
1752
+ if (!prevItems.includes(event)) {
1753
+ return [...prevItems, event];
1754
+ }
1755
+ return prevItems;
1756
+ });
1757
+ };
1758
+
1759
+ return (
1760
+ <>
1761
+ <div className='table-custom custom-list-journal-body-table'>
1762
+ <div className="table-responsive">
1763
+ <table className="table">
1764
+ <thead className='thead_'>
1765
+ <tr>
1766
+ <th className={`text-center ${props.side_of_icon === "gauche" ? "d-block" : "d-none"}`}></th>
1767
+ {props.arr && props.arr.map((item, index) => (
1768
+ <th className='text-center' key={`trg${index}rhyt${item.label}`}>{item.label}</th>
1769
+ ))}
1770
+ <th className={`text-center ${props.side_of_icon === "droite" ? "d-block" : "d-none"}`}></th>
1771
+ </tr>
1772
+ </thead>
1773
+ <tbody className='tbody_'>
1774
+ {rows.map((data, index, array) => {
1775
+ const isLastRow = index === array.length - 1;
1776
+ const icon = isLastRow ? (
1777
+ <LuPlusCircle role='button' title="Double click for delete" className="icon green" size={20} onClick={() => {
1778
+ setRawValues(prev => [...prev, newRowValues])
1779
+ handleResetNewRow()
1780
+ handleAddRow()
1781
+ }} />
1782
+ ) : (
1783
+ <FaTrashAlt className='icon red delete-icon' onDoubleClick={() => {
1784
+ handleDeleteRowValues(data.id)
1785
+ handleDeleteRow(data.id)
1786
+ }} />
1787
+ );
1788
+ return (
1789
+ <tr key={`${index}Fsrsdcr`}>
1790
+ <td className={`text-center ${props.side_of_icon === "gauche" ? "d-block" : "d-none"} p-2`} >
1791
+ {icon}
1792
+ </td>
1793
+ {props.arr && props.arr.map((item, index) => (
1794
+ <td className='text-center p-1'>
1795
+ {
1796
+ item.type === "list" ? (
1797
+ <CustomSelector
1798
+ optionsInitial={item.list.map(item2 => ({
1799
+ id: item2.id,
1800
+ value: item2.value,
1801
+ label: item2.label
1802
+ }))}
1803
+ onSelect={(selectedValue) => handleSelectedItem(selectedValue)}
1804
+ placeholder={'Select'}
1805
+ loading={null}
1806
+ />
1807
+ ) : (
1808
+ <input className={`w-100 ${item.type === "checkbox" ? "mx-auto" : ""}`} name={item.name} type={item.type} onChange={(e) => handleChangeNewRow(e)} />
1809
+ )
1810
+ }
1811
+ </td>
1812
+ ))}
1813
+ <td className={`text-center ${props.side_of_icon === "droite" ? "d-block" : "d-none"} p-2`}>
1814
+ {icon}
1815
+ </td>
1816
+ </tr>
1817
+ )
1818
+ })}
1819
+ </tbody>
1820
+ </table>
1821
+ </div>
1822
+ </div>
1823
+ </>
1824
+ );
1825
+ };
1826
+
1827
+ export const FTable: React.FC<FTableProps> = (props) => {
1828
+
1829
+
1830
+
1831
+
1832
+ // ---------------------------------------------------------------------------
1833
+ // ---------------------------------CUSTOM HOOKS------------------------------
1834
+ // ---------------------------------------------------------------------------
1835
+
1836
+ const navigate = useNavigate();
1837
+ const { center } = useCenter();
1838
+ const { language } = useLanguage();
1839
+ const { setIsLoadingCancelable } = useLoading()
1840
+
1841
+ // ---------------------------------------------------------------------------
1842
+ // ---------------------------------------------------------------------------
1843
+
1844
+ // ---------------------------------------------------------------------------
1845
+ // ---------------------------------REFS--------------------------------------
1846
+ // ---------------------------------------------------------------------------
1847
+
1848
+ const champsRef = useRef([]);
1849
+ const champsRef2 = useRef([]);
1850
+ const searchSelect = useRef();
1851
+ const btnsContainer = useRef();
1852
+ const sweetAlertRef = useRef();
1853
+ const tableRefExportExcel = useRef(null);
1854
+
1855
+ // ---------------------------------------------------------------------------
1856
+ // ---------------------------------------------------------------------------
1857
+
1858
+
1859
+
1860
+
1861
+
1862
+ // ---------------------------------------------------------------------------
1863
+ // ---------------------------------DEFAULT PROPS-----------------------------
1864
+ // ---------------------------------------------------------------------------
1865
+
1866
+ const selected = props.selected ?? []
1867
+
1868
+ const setSelected = props.setSelected ?? ((param) => "")
1869
+
1870
+ const maxText = props.maxText ?? 75
1871
+
1872
+ const type = props.type ?? "table"
1873
+
1874
+ const noFilter = props.noFilter ?? false
1875
+
1876
+ const noSearch = props.noSearch ?? false
1877
+
1878
+ const table_type = props.table_type ?? "basic"
1879
+
1880
+ const rerender = props.toggle ?? "";
1881
+
1882
+ const headers = props.headers ?? [];
1883
+
1884
+ const totalHeaders = props.totalHeaders ?? [];
1885
+
1886
+ const btnsList = props.btnsList ?? [];
1887
+
1888
+ // ---------------------------------------------------------------------------
1889
+ // ---------------------------------------------------------------------------
1890
+
1891
+
1892
+
1893
+
1894
+
1895
+
1896
+
1897
+ // ---------------------------------------------------------------------------
1898
+ // ---------------------------------STATES-----------------------------------
1899
+ // ---------------------------------------------------------------------------
1900
+
1901
+ const [lineSelected, setLineSelected] = useState('')
1902
+ const [showContent, setShowContent] = useState({});
1903
+ const [showModalComment, setShowModalComment] = useState(false);
1904
+ const [secondInfoComment, setSecondInfoComment] = useState("");
1905
+ const [comment, setComment] = useState(false);
1906
+ const [showLeadForm, setShowLeadForm] = useState(false);
1907
+ const [isSorting, setIsSorting] = useState(props.orderByDefault);
1908
+ const [data, setData] = useState([]);
1909
+ const [dataFilters, setDataFilters] = useState([]);
1910
+ const [noFiltres, setNoFiltres] = useState(false);
1911
+ const [showFiltres, setShowFiltres] = useState(false);
1912
+ const [showRecherche, setShowRecherche] = useState(false);
1913
+ const [showFR, setShowFR] = useState("");
1914
+ const [filtres, setFiltres] = useState([]);
1915
+ const [csv, setCsv] = useState("");
1916
+ const [legends, setLegends] = useState([]);
1917
+ const [listFiltres, setListFiltres] = useState([]);
1918
+ const [loading, setLoading] = useState(true);
1919
+ const [loading2, setLoading2] = useState(true);
1920
+ const [queryURL, setQueryURL] = useState('');
1921
+ const [showCommunication, setShowCommunication] = useState(false);
1922
+ const [showAttachments, setShowAttachments] = useState(false);
1923
+ const [showImportExcel, setShowImportExcel] = useState(false);
1924
+ const [attachments, setAttachments] = useState([])
1925
+ const [search, setSearch] = useState('');
1926
+ const [selectedItems, setSelectedItems] = useState([]);
1927
+ const [btnAdd, setBtnAdd] = useState(true);
1928
+ const [reponseDetail, setReponseDetail] = useState({
1929
+ total_pages: null,
1930
+ previous: null,
1931
+ next: null,
1932
+ current_page: null,
1933
+
1934
+ });
1935
+
1936
+ const makeFilters = () => {
1937
+ const newFilters = headers.reduce((acc, item) => {
1938
+ if (item.formule) {
1939
+ acc[`${item.search_name}__icontains`] = ""
1940
+ return acc
1941
+ } else {
1942
+ acc[`${item.key}__icontains`] = ""
1943
+ return acc
1944
+ }
1945
+ })
1946
+ return newFilters
1947
+ }
1948
+ const preFilters = (headers.length) > 0 ? makeFilters() : []
1949
+
1950
+ const [filters, setFilters] = useState({
1951
+ ...preFilters,
1952
+ center_id: center?.id ?? null,
1953
+ order_by: props.orderByDefault ?? "id",
1954
+ page: 1
1955
+ })
1956
+
1957
+ const [filtersMemo, setFiltersMemo] = useState({
1958
+ ...preFilters,
1959
+ center_id: center?.id ?? null,
1960
+ order_by: props.orderByDefault ?? "id",
1961
+ page: 1
1962
+ })
1963
+
1964
+ // ---------------------------------------------------------------------------
1965
+ // ---------------------------------------------------------------------------
1966
+
1967
+
1968
+
1969
+
1970
+
1971
+
1972
+ const handleClick = (value) => {
1973
+ if (showFR === value) {
1974
+ setShowFR('')
1975
+ } else {
1976
+ setShowFR(value);
1977
+ }
1978
+ };
1979
+
1980
+ const getDataFilter = async () => {
1981
+ if (props.api) {
1982
+ setLoading(true)
1983
+ try {
1984
+ const response = await fetch(queryURL, {
1985
+ method: 'GET',
1986
+ headers: authenticated_header()
1987
+ });
1988
+
1989
+ if (!response.ok) {
1990
+ throw new Error(`HTTP ERROR! STATUS: ${response.status}`);
1991
+ }
1992
+
1993
+ const data = await response.json();
1994
+ setReponseDetail(data)
1995
+ setDataFilters(data.results)
1996
+ setLoading(false)
1997
+ } catch (error) {
1998
+ console.error('ERROR FETCHING DATA', error);
1999
+ }
2000
+ }
2001
+ };
2002
+
2003
+ const getData = async () => {
2004
+ if (props.api) {
2005
+ setLoading2(true)
2006
+ try {
2007
+ const response = await fetch(`${addressIpApi}${props.api}?all=true&center_id=${center?.id ?? null}${props.additional_params ?? ''}`, {
2008
+ method: 'GET',
2009
+ headers: authenticated_header()
2010
+ });
2011
+
2012
+ if (!response.ok) {
2013
+ throw new Error('Error fetching data');
2014
+ }
2015
+
2016
+ const data = await response.json();
2017
+ setData(data.data)
2018
+ const listFilter = []
2019
+ for (let idx = 0; idx < headers.length; idx++) {
2020
+ if (headers[idx].filterable) {
2021
+ listFilter.push(data.data.map(item => ({ id: item.id, name: headers[idx].formule ? headers[idx].formule(item) : item[`${headers[idx].key}`] })))
2022
+ }
2023
+ }
2024
+ setListFiltres(listFilter)
2025
+ setLoading2(false)
2026
+
2027
+ } catch (error) {
2028
+ console.error('Error fetching data:', error);
2029
+ }
2030
+ }
2031
+ }
2032
+
2033
+
2034
+
2035
+
2036
+
2037
+ useEffect(() => {
2038
+ const handleClickOutside = (event) => {
2039
+ if (!event.target.classList.contains("selected")) {
2040
+ setLineSelected('');
2041
+ }
2042
+ };
2043
+
2044
+ document.addEventListener('mousedown', handleClickOutside);
2045
+
2046
+ return () => {
2047
+ document.removeEventListener('mousedown', handleClickOutside);
2048
+ };
2049
+ }, []);
2050
+
2051
+ useEffect(() => {
2052
+ const params = new URLSearchParams(filters).toString();
2053
+ setQueryURL(`${addressIpApi}${props.api}?${params}${props.additional_params ?? ''}`);
2054
+ }, [filters]);
2055
+
2056
+ useEffect(() => {
2057
+ getData();
2058
+ }, [center, rerender]);
2059
+
2060
+ useEffect(() => {
2061
+ getDataFilter();
2062
+ }, [center, queryURL, rerender]);
2063
+
2064
+ useEffect(() => {
2065
+ const columns = []
2066
+ headers.forEach(item1 => item1.filterable && columns.push({ label: item1.name, type: item1.type, key: item1.search_name ? item1.search_name : item1.key }))
2067
+ setFiltres(columns)
2068
+ props.btn_add !== undefined && setBtnAdd(props.btn_add)
2069
+ }, [])
2070
+
2071
+ // ---------------------------------------------------------------------------
2072
+ // ---------------------------------------------------------------------------
2073
+
2074
+ return (
2075
+ <>
2076
+ <section
2077
+ id={props.id}
2078
+ className={`ftable-container flex-col`}
2079
+ >
2080
+ <div className={`gap-2 flex-col p-1`}>
2081
+ <div className={`col`}>
2082
+
2083
+ <div className='table-custom custom-list-journal-body-table'>
2084
+ <div className={`table-responsive type-table ${props.inWorkspace && "in-workspace"} ${props.wTotalCard && "w-total-card"} ${props.wMenu && "w-menu"}`}>
2085
+
2086
+ {
2087
+ table_type === "basic" && (
2088
+ <TableBasic
2089
+ actions={props.actions}
2090
+ dataFilters={dataFilters}
2091
+ loading={loading}
2092
+ maxText={maxText}
2093
+ data={data}
2094
+ headers={headers}
2095
+ totalHeaders={totalHeaders}
2096
+ filters={filters}
2097
+ setFilters={setFilters}
2098
+ setComment={setComment}
2099
+ setSecondInfoComment={setSecondInfoComment}
2100
+ setShowModalComment={setShowModalComment}
2101
+ lineSelected={lineSelected}
2102
+ setLineSelected={setLineSelected}
2103
+ isSorting={isSorting}
2104
+ setIsSorting={setIsSorting}
2105
+ />
2106
+ )
2107
+ }
2108
+ </div>
2109
+ </div>
2110
+
2111
+ </div>
2112
+
2113
+ <div className="w-100 flex-between align-end">
2114
+ {
2115
+ !props.noPagination && dataFilters.length > 0 ? <FilterPagination filters={[filters, setFilters]} reponseDetail={reponseDetail} setQueryURL={setQueryURL} /> : <div className=""></div>
2116
+ }
2117
+
2118
+ {
2119
+ props.summary && <FooterSummary colonnes={props.summary.colonnes} rows={props.summary.rows} param={data} />
2120
+ }
2121
+ </div>
2122
+ </div>
2123
+ </section>
2124
+ {/* ------------------------------------------------------------------------------------- */}
2125
+ {/* ---------------------------------MODAL COMMENTAIRE----------------------------------- */}
2126
+ <FModal
2127
+ type="basic"
2128
+ show={showModalComment}
2129
+ hide={() => setShowModalComment(false)}
2130
+ title={DICTIONNARY.Commentaire[language]}
2131
+ modalClassName={"modal-format-com"}
2132
+ secondInfo={secondInfoComment}
2133
+ wBtns={false}
2134
+ children={
2135
+ <>
2136
+ <div className="col radius-1 p-2 bordered">
2137
+ {comment}
2138
+ </div>
2139
+ </>
2140
+ }
2141
+ />
2142
+ {/* ------------------------------------------------------------------------------------- */}
2143
+ </>
2144
+ );
2145
+ }
2146
+
2147
+
2148
+
2149
+ export const TableBasic: React.FC<any> = (props) => {
2150
+
2151
+ const dataFilters = props.dataFilters ?? []
2152
+
2153
+ const loading = props.loading ?? true
2154
+
2155
+ const maxText = props.maxText ?? 75
2156
+
2157
+ const data = props.data ?? [];
2158
+
2159
+ const headers = props.headers ?? [];
2160
+
2161
+ const totalHeaders = props.totalHeaders ?? [];
2162
+
2163
+ const filters = props.filters ?? {};
2164
+
2165
+ const setFilters = props.setFilters ?? ((param) => "");
2166
+
2167
+ const setComment = props.setComment ?? ((param) => "");
2168
+
2169
+ const setSecondInfoComment = props.setSecondInfoComment ?? ((param) => "");
2170
+
2171
+ const setShowModalComment = props.setShowModalComment ?? ((param) => "");
2172
+
2173
+ const lineSelected = props.lineSelected ?? "";
2174
+ const setLineSelected = props.setLineSelected ?? ((param) => "");
2175
+
2176
+ const isSorting = props.isSorting;
2177
+ const setIsSorting = props.setIsSorting ?? ((param) => "");
2178
+
2179
+ const { language } = useLanguage()
2180
+ const {center} = useCenter()
2181
+
2182
+ return (
2183
+ <>
2184
+ <table className="table">
2185
+ <thead className="table-dark thead_">
2186
+ <tr>
2187
+ {
2188
+ headers.map((item, index, array) => (
2189
+ <th
2190
+ className={`${(isSorting === item.key || isSorting === `-${item.key}`) && "bg-cararra"} ${!item.sortable && "no-sortable"}`}
2191
+ >
2192
+ {
2193
+ item.sortable ? (
2194
+ <>
2195
+ <div
2196
+ className="align-center gap-1"
2197
+ data-bs-toggle="dropdown"
2198
+ aria-expanded="false"
2199
+ title={item.abbr ?? ""}
2200
+ >
2201
+ {item.name}
2202
+ <FaSort className='icon rem-075 gray' />
2203
+ </div>
2204
+ <ul className="dropdown-menu sort-menu">
2205
+ <li>
2206
+ <span
2207
+ className={`${isSorting === item.key && "sorted"} dropdown-item`}
2208
+ onClick={() => {
2209
+ if (isSorting === item.key) {
2210
+ return ""
2211
+ } else if (isSorting !== item.key) {
2212
+ const copy = {
2213
+ ...filters,
2214
+ order_by: item.key,
2215
+ }
2216
+ setIsSorting(item.key)
2217
+ setFilters(copy)
2218
+ }
2219
+ }}
2220
+ >
2221
+ <FaSortAmountDownAlt />
2222
+ Ascending
2223
+ </span>
2224
+ </li>
2225
+ <li>
2226
+ <span
2227
+ className={`${isSorting === `-${item.key}` && "sorted"} dropdown-item`}
2228
+ onClick={() => {
2229
+ if (isSorting !== `-${item.key}`) {
2230
+ const copy = {
2231
+ ...filters,
2232
+ order_by: `-${item.key}`,
2233
+ }
2234
+ setIsSorting(`-${item.key}`)
2235
+ setFilters(copy)
2236
+ } else if (isSorting === `-${item.key}`) {
2237
+ return ""
2238
+ }
2239
+ }}
2240
+ >
2241
+ <FaSortAmountDown />
2242
+ Descending
2243
+ </span>
2244
+ </li>
2245
+ </ul>
2246
+ </>
2247
+ ) : (
2248
+ item.name
2249
+ )
2250
+ }
2251
+ <div className="resizer"></div>
2252
+ </th>
2253
+ ))
2254
+ }
2255
+ {
2256
+ props.actions && props.actions && <th></th>
2257
+ }
2258
+ </tr>
2259
+ </thead>
2260
+ <tbody className='tbody_'>
2261
+ {loading ? (
2262
+ <tr className='no-b no-hover'>
2263
+ <td colSpan={props.actions ? headers.length + 1 : headers.length} className="text-center no-b p-2">...chargement</td>
2264
+ </tr>
2265
+ ) : dataFilters.length > 0 ? (
2266
+ dataFilters.map((row) => (
2267
+ <tr key={row.id} className={`${lineSelected === row.id && "selected"}`}>
2268
+ {
2269
+ headers.map((item, index, array) =>
2270
+ item.type === "date" ? (
2271
+ <td key={item.key} className=''>
2272
+ {
2273
+ item.formule ? item.formule(row) !== "-" ? DATETIME.formatDDMMYYYY(item.formule(row)) : "-" : row[`${item.key}`] !== "-" ? DATETIME.formatDDMMYYYY(row[`${item.key}`]) : "-"
2274
+ }
2275
+ </td>
2276
+ ) : (
2277
+ item.type === "comment" ? (
2278
+ <td className='' key={item.key}>
2279
+ {
2280
+ item.formule ? (
2281
+ <>
2282
+ {item.formule(row) ? (
2283
+ <img src={table_com_icon2} alt='icon' title='click ' role='button' className='mx-auto icon spe-img' onClick={() => { setComment(item.formule(row)); setSecondInfoComment(item.formuleForComment ? item.formuleForComment(row) : ""); setShowModalComment(true) }} />
2284
+ ) : (
2285
+ <img src={table_com_icon} alt='icon' className='mx-auto icon spe-img' />
2286
+ )}
2287
+ </>
2288
+ ) :
2289
+ <>
2290
+ {row[`${item.key}`] ? (
2291
+ <img src={table_com_icon2} alt='icon' role='button' className='mx-auto icon spe-img' onClick={() => { setComment(row[`${item.key}`]); setSecondInfoComment(item.formuleForComment ? item.formuleForComment(row) : ""); setShowModalComment(true) }} />
2292
+ ) : (
2293
+ <img src={table_com_icon} alt='icon' className='mx-auto icon spe-img' />
2294
+ )}
2295
+ </>}
2296
+ </td>
2297
+ ) : (
2298
+ item.type === "choice" ? (
2299
+ <td key={item.key} className='ps-1'>
2300
+ {
2301
+ item.formule ? (
2302
+ <div className="mx-auto" title={item.formule(row)[language]}>
2303
+ {
2304
+ item.formule(row).icon
2305
+ }
2306
+ </div>
2307
+ ) : "-"
2308
+ }
2309
+ </td>
2310
+ ) : (
2311
+ item.type === "amount" ? (
2312
+ <td key={item.key} className='ps-1'>
2313
+ {
2314
+ item.formule ? item.formule(row) : sep_mil(row[`${item.key}`])
2315
+ }
2316
+ </td>
2317
+ ) : (
2318
+ item.type === "description" ? (
2319
+ <td key={item.key} className='ps-1'>
2320
+ {
2321
+ item.formule ? (
2322
+ item.formule(row).length < maxText ? (
2323
+ item.formule(row)
2324
+ ) : (
2325
+ <FDropdown
2326
+ trigger={STRING.truncateText(item.formule(row), maxText ?? 75)}
2327
+ content={item.formule(row)}
2328
+ />
2329
+ )
2330
+ ) : (
2331
+ row[`${item.key}`].length < 75 ? (
2332
+ row[`${item.key}`]
2333
+ ) : (
2334
+ <FDropdown
2335
+ trigger={STRING.truncateText(row[`${item.key}`], maxText ?? 75)}
2336
+ content={row[`${item.key}`]}
2337
+ />
2338
+ )
2339
+ )
2340
+ }
2341
+ </td>
2342
+ ) : (
2343
+ <td key={item.key} className=''>
2344
+ {
2345
+ item.formule ? item.formule(row) : row[`${item.key}`]
2346
+ }
2347
+ </td>
2348
+ )
2349
+ ))))
2350
+ )
2351
+ }
2352
+ {
2353
+ props.actions && props.actions && (
2354
+ <td className='text-center'>
2355
+ <div className="" onClick={() => setLineSelected(row.id)}>
2356
+ <span className="doropdown-vertical-icon"
2357
+ data-bs-toggle="dropdown"
2358
+ aria-expanded="false">
2359
+ <BsThreeDotsVertical />
2360
+ </span>
2361
+ <ul className="dropdown-menu dropdown-menu-right">
2362
+ {props.actions?.map((act) => {
2363
+ const link = act.to ? act.to(row) : "";
2364
+ const hasPermission = !act.permission || center?.user_permissions?.includes(act.permission);
2365
+ const handleClick = () => {
2366
+ if (!hasPermission) return; // Empêche l'action si pas autorisé
2367
+ setLineSelected("");
2368
+ act.onclick?.(row);
2369
+ };
2370
+
2371
+ if (act.condition && !act.condition(row)) return null;
2372
+
2373
+ return (
2374
+ <li key={act.label}>
2375
+ {act.onclick ? (
2376
+ <span
2377
+ className={`dropdown-item ${!hasPermission ? 'wise-disabled text-muted' : ''}`}
2378
+ onClick={handleClick}
2379
+ title={!hasPermission ? "Vous n'avez pas l'autorisation requise pour cette action." : ""}
2380
+ style={{ cursor: hasPermission ? "pointer" : "not-allowed" }}
2381
+ >
2382
+ {act.label}
2383
+ </span>
2384
+ ) : (
2385
+ <Link
2386
+ className={`dropdown-item ${!hasPermission ? 'wise-disabled text-muted' : ''}`}
2387
+ to={hasPermission ? link : "#"}
2388
+ title={!hasPermission ? "Vous n'avez pas l'autorisation requise pour cette action." : ""}
2389
+ onClick={(e) => {
2390
+ if (!hasPermission) e.preventDefault();
2391
+ else setLineSelected("");
2392
+ }}
2393
+ style={{ cursor: hasPermission ? "pointer" : "not-allowed" }}
2394
+ >
2395
+ {act.label}
2396
+ </Link>
2397
+ )}
2398
+ </li>
2399
+ );
2400
+ })}
2401
+ </ul>
2402
+ </div>
2403
+ </td>
2404
+ )
2405
+ }
2406
+ </tr>
2407
+ ))
2408
+ ) : (
2409
+ <tr className='no-b no-hover'>
2410
+ <td
2411
+ colSpan={headers.length}
2412
+ className="no-b no-hover"
2413
+ >
2414
+ <div className="col w-100 d-flex justify-content-center align-items-center">
2415
+ <img className="spe-illus-nodata" src={nodata_illus} alt="illus" />
2416
+ </div>
2417
+ </td>
2418
+ </tr>
2419
+ )}
2420
+ </tbody>
2421
+ {
2422
+ !loading && dataFilters.length > 0 && totalHeaders.length > 0 && (
2423
+ <tfoot className="f-tfoot_">
2424
+ <tr>
2425
+ {
2426
+ totalHeaders.map((item, index) => (
2427
+ <td
2428
+ key={`dsqf${index}mflkdgop`}
2429
+ colSpan={item.colSpan ?? 1}
2430
+ >
2431
+ {item.formule(data) ?? ""}
2432
+ </td>
2433
+ ))
2434
+ }
2435
+ {
2436
+ props.actions && (
2437
+ <td className=''>
2438
+ </td>
2439
+ )
2440
+ }
2441
+ </tr>
2442
+ </tfoot>
2443
+ )
2444
+ }
2445
+ </table>
2446
+ </>
2447
+ );
2448
+ }