@asaleh37/ui-base 25.12.121 → 25.12.123

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.
@@ -1,1047 +1,1047 @@
1
- import {
2
- DataGridPremium,
3
- GridActionsCellItem,
4
- GridActionsCellItemProps,
5
- GridColumnOrderChangeParams,
6
- GridColumnResizeParams,
7
- GridColumnVisibilityModel,
8
- GridEventListener,
9
- GridExpandMoreIcon,
10
- GridPinnedColumnFields,
11
- GridRowGroupingModel,
12
- GridRowModes,
13
- GridRowModesModel,
14
- GridRowParams,
15
- GridRowSelectionModel,
16
- } from "@mui/x-data-grid-premium";
17
- import { TemplateGridColDef, TemplateGridProps } from "../DataEntryTypes";
18
- import TemplateGridTopBar from "./TemplateGridTopBar";
19
- import { useNavigate } from "react-router-dom";
20
- import React, { useEffect, useState } from "react";
21
- import { z } from "zod";
22
- import {
23
- constructGridColumnsFromFields,
24
- constructValidationSchema,
25
- getAllFields,
26
- } from "../DataEntryUtil";
27
- import { toast } from "react-toastify";
28
- import {
29
- Accordion,
30
- AccordionDetails,
31
- AccordionSummary,
32
- Box,
33
- Divider,
34
- IconButton,
35
- Tooltip,
36
- Typography,
37
- } from "@mui/material";
38
- import { capitalizeFirstLetter, isNumber } from "../../../../util/AppUtils";
39
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
40
- import TemplateGridRecordAction from "./TemplateGridRecordAction";
41
- import TemplateForm from "../TemplateDataForm/TemplateForm";
42
- import { useSelector } from "react-redux";
43
- import { useTranslation } from "react-i18next";
44
- import useSession from "../../../../hooks/UseSession";
45
- import { useConfirmationWindow } from "../../../../hooks/UseConfirmationWindow";
46
- import { useWindow } from "../../../../hooks/UseWindow";
47
- import { useAxios } from "../../../../hooks";
48
- import useLookupGridColumn from "../../../../hooks/useLookupGridColumn";
49
- import { ContinuousColorLegend } from "@mui/x-charts";
50
- import AttachmentPanel from "../../attachment/AttachmentPanel";
51
- import WorkflowDocumentPanel from "../../workflow/WorkflowDocumentPanel";
52
- let currentNewRecordIndex = -1;
53
-
54
- interface GridState {
55
- columnVisibilityModel?: GridColumnVisibilityModel;
56
- columnOrder?: string[];
57
- columnWidths?: { [key: string]: number };
58
- columnGroupingModel?: GridRowGroupingModel;
59
- pinnedColumns?: GridPinnedColumnFields;
60
- }
61
-
62
- const loadGridState = (gridStateKey: string): GridState => {
63
- try {
64
- const saved = localStorage.getItem(gridStateKey);
65
- return saved ? JSON.parse(saved) : {};
66
- } catch (e) {
67
- console.error("Failed to load grid state", e);
68
- return {};
69
- }
70
- };
71
-
72
- const saveGridState = (gridStateKey: string, statePart: GridState) => {
73
- const current = loadGridState(gridStateKey);
74
- localStorage.setItem(
75
- gridStateKey,
76
- JSON.stringify({ ...current, ...statePart })
77
- );
78
- };
79
-
80
- const PIN_FIXED_COLUMNS = ["__check__", "actions"];
81
-
82
- const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
83
- const { t } = useTranslation();
84
- const { handleGetRequest } = useAxios();
85
- const stores = useSelector((state: any) => state.commonStores.stores);
86
- const AppLayout = useSelector((state: any) => state.AppLayout);
87
- const [selectedRecord, setSelectedRecord] = useState<any>({});
88
- const [attachmentPanelEnabledForRecord, setAttachmentPanelEnabledForRecord] =
89
- useState<boolean>(true);
90
- const { getLookupOptions } = useLookupGridColumn();
91
- const { Window: AttachmentWindow, setWindowState: setAttachmentWindowState } =
92
- useWindow({
93
- windowTitle: t(props.gridTitle) + " Attachments",
94
- windowIcon: "paperclip",
95
- width: "fit-content",
96
- height: "fit-content",
97
- minHeight: 500,
98
- minWidth: "50%",
99
- onCloseCallBack: () => {
100
- props?.apiActions?.reloadData(props.gridLoadParametersValues);
101
- },
102
- });
103
- const { Window: WorkFlowWindow, setWindowState: setWorkFlowWindowState } =
104
- useWindow({
105
- windowTitle: t(props.gridTitle) + " Approvals",
106
- windowIcon: "stamp",
107
- height: "fit-content",
108
- minHeight: 500,
109
- width: "fit-content",
110
- // width:1100,
111
- onCloseCallBack: () => {
112
- props?.apiActions?.reloadData(props.gridLoadParametersValues);
113
- },
114
- });
115
- const [generatedColumns, setGeneratedColumns] = useState<
116
- Array<TemplateGridColDef>
117
- >([]);
118
- const fields = getAllFields(props.formElements);
119
- const hiddenFields = [];
120
- const savedState = React.useMemo<GridState>(
121
- () => (props?.gridStateKey ? loadGridState(props.gridStateKey) : {}),
122
- []
123
- );
124
- for (const field of fields) {
125
- if (field?.gridProps?.hidden === true) {
126
- hiddenFields.push(field.fieldName);
127
- }
128
- }
129
- let initialVisibilityState: GridColumnVisibilityModel = {};
130
- if (savedState?.columnVisibilityModel) {
131
- initialVisibilityState = savedState.columnVisibilityModel;
132
- const existingFields = Object.keys(initialVisibilityState);
133
- for (const field of hiddenFields) {
134
- if (!existingFields.includes(field)) {
135
- initialVisibilityState[field] = false;
136
- }
137
- }
138
- } else {
139
- for (const field of hiddenFields) {
140
- initialVisibilityState[field] = false;
141
- }
142
- }
143
-
144
- const themeDirection = useSelector(
145
- (state: any) => state.AppLayout.appDirection
146
- );
147
-
148
- const clearGridState = () => {
149
- if (props?.gridStateKey) {
150
- localStorage.removeItem(props.gridStateKey);
151
- }
152
- setColumnVisibilityModel({});
153
- setColumnOrder([]);
154
- setColumnWidths({});
155
- setGridRowGroupingModel([]);
156
- setPinnedColumns({
157
- left: [...(themeDirection === "ltr" ? PIN_FIXED_COLUMNS : [])],
158
- right: [...(themeDirection === "rtl" ? PIN_FIXED_COLUMNS : [])],
159
- });
160
- };
161
- const [columnVisibilityModel, setColumnVisibilityModel] =
162
- React.useState<GridColumnVisibilityModel>(initialVisibilityState);
163
- const [columnOrder, setColumnOrder] = React.useState<string[]>(
164
- savedState.columnOrder || []
165
- );
166
- const [columnWidths, setColumnWidths] = React.useState<{
167
- [key: string]: number;
168
- }>(savedState.columnWidths || {});
169
- const [gridRowGroupingModel, setGridRowGroupingModel] =
170
- useState<GridRowGroupingModel>(savedState.columnGroupingModel || []);
171
- let newLeft = savedState?.pinnedColumns?.left || [];
172
- newLeft = newLeft.filter(
173
- (record: any) => !PIN_FIXED_COLUMNS.includes(record)
174
- );
175
- let newRight = savedState?.pinnedColumns?.right || [] || [];
176
- newRight = newRight.filter(
177
- (record: any) => !PIN_FIXED_COLUMNS.includes(record)
178
- );
179
-
180
- const [pinnedColumns, setPinnedColumns] = useState<GridPinnedColumnFields>({
181
- left: [...(themeDirection === "ltr" ? PIN_FIXED_COLUMNS : []), ...newLeft],
182
- right: [
183
- ...(themeDirection === "rtl" ? PIN_FIXED_COLUMNS : []),
184
- ...newRight,
185
- ],
186
- });
187
-
188
- const session = useSession();
189
- const navigate = useNavigate();
190
- const [recordToDelete, setRecordToDelete] = useState<any>(null);
191
- const [recordToEdit, setRecordToEdit] = useState<any>(null);
192
- const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
193
- const loadComboboxData: (
194
- storeUrl: any,
195
- storeLoadParam: any,
196
- dataQueryId: any
197
- ) => Promise<Array<any>> = async (storeUrl, storeLoadParam, dataQueryId) => {
198
- let comboboxData: Array<any> = [];
199
- if (storeUrl) {
200
- await handleGetRequest({
201
- endPointURI: storeUrl,
202
- showMask: false,
203
- parameters: storeLoadParam,
204
- successCallBkFn: (response) => {
205
- comboboxData = response.data;
206
- },
207
- });
208
- } else if (dataQueryId) {
209
- await handleGetRequest({
210
- endPointURI: "api/v1/dev/query/result",
211
- showMask: false,
212
- parameters: { queryId: dataQueryId, ...storeLoadParam },
213
- successCallBkFn: (response) => {
214
- comboboxData = response.data;
215
- },
216
- });
217
- }
218
- return comboboxData;
219
- };
220
- const adjustGridColumns = async () => {
221
- let gridColumns = constructGridColumnsFromFields(
222
- fields,
223
- props?.editMode?.editMode === "row" || false,
224
- t
225
- );
226
- for (let gridColumn of gridColumns) {
227
- if (gridColumn?.lookupType) {
228
- gridColumn.displayField =
229
- AppLayout.appDirection === "ltr"
230
- ? "lookupEnDisplay"
231
- : "lookupArDisplay";
232
- gridColumn.options = await getLookupOptions(gridColumn.lookupType);
233
- gridColumn.valueField = "lookupValue";
234
- }
235
- if (gridColumn.type === "custom") {
236
- debugger;
237
- if (gridColumn?.options) {
238
- continue;
239
- } else if (gridColumn?.commonStoreKey) {
240
- gridColumn.options = stores[gridColumn.commonStoreKey]?.data || [];
241
- } else {
242
- gridColumn.options = await loadComboboxData(
243
- gridColumn?.storeUrl,
244
- gridColumn?.storeLoadParam,
245
- gridColumn?.dataQueryId
246
- );
247
- }
248
- }
249
- }
250
- setGeneratedColumns(gridColumns);
251
- };
252
- const [rowSelectionModel, setRowSelectionModel] =
253
- useState<GridRowSelectionModel>({ ids: new Set(), type: "include" });
254
- let keyColumnName: string = "id";
255
- if (props?.keyColumnName) {
256
- keyColumnName = props?.keyColumnName;
257
- }
258
- const setData = props.setData;
259
- let validationSchema = null;
260
- if (props?.validationSchema) {
261
- validationSchema = props.validationSchema;
262
- } else {
263
- validationSchema = z.object(constructValidationSchema(fields));
264
- }
265
-
266
- let isEditAllowed = true;
267
- if (props?.editAction?.authority) {
268
- isEditAllowed = session.isUserAuthorized(props.editAction.authority);
269
- }
270
- let isDeleteAllowed = true;
271
- if (props?.deleteAction?.authority) {
272
- isDeleteAllowed = session.isUserAuthorized(props.deleteAction.authority);
273
- }
274
-
275
- const handleRowModesModelChange = (newModel: GridRowModesModel) => {
276
- setRowModesModel(newModel);
277
- };
278
-
279
- const handlePinnedColumnsChange = (newModel: GridPinnedColumnFields) => {
280
- let newLeft = newModel?.left || [];
281
- newLeft = newLeft.filter(
282
- (record: any) => !PIN_FIXED_COLUMNS.includes(record)
283
- );
284
- let newRight = newModel?.right || [];
285
- newRight = newRight.filter(
286
- (record: any) => !PIN_FIXED_COLUMNS.includes(record)
287
- );
288
- const newPinedColumns: GridPinnedColumnFields = {
289
- left: [
290
- ...(themeDirection === "ltr" ? PIN_FIXED_COLUMNS : []),
291
- ...newLeft,
292
- ],
293
- right: [
294
- ...(themeDirection === "rtl" ? PIN_FIXED_COLUMNS : []),
295
- ...newRight,
296
- ],
297
- };
298
- setPinnedColumns(newPinedColumns);
299
- if (props?.gridStateKey) {
300
- saveGridState(props.gridStateKey, { pinnedColumns: newPinedColumns });
301
- }
302
- };
303
-
304
- const handleDeleteRecord = async () => {
305
- let result = true;
306
- if (!(recordToDelete && recordToDelete?.isNew === true)) {
307
- result = await props.apiActions.deleteRecordById(
308
- recordToDelete[keyColumnName]
309
- );
310
- }
311
- if (result) {
312
- if (props?.deleteAction?.postActionCallBack) {
313
- await props.deleteAction.postActionCallBack(recordToDelete);
314
- }
315
-
316
- setData((oldData: any) => {
317
- const newData = oldData.filter(
318
- (record: any) =>
319
- record[keyColumnName] != recordToDelete[keyColumnName]
320
- );
321
- return newData;
322
- });
323
- }
324
- };
325
-
326
- const { ConfirmationWindow, setOpen: setConfirmationWindowOpened } =
327
- useConfirmationWindow({
328
- title: "Confirmation",
329
- body: "Are you sure you want to delete this record ?",
330
- onConfirmationCallBk: handleDeleteRecord,
331
- });
332
-
333
- let formModalHeight = undefined;
334
- let formModalWidth = undefined;
335
- let formModalMinHeight = undefined;
336
- let formModalMinWidth = undefined;
337
- let formModalIcon = undefined;
338
- let formModalTitle = undefined;
339
-
340
- if (props?.editMode?.editMode === "modal") {
341
- formModalHeight = props?.editMode?.specs?.modalHeight || "fit-content";
342
- formModalWidth = props?.editMode?.specs?.modalWidth || "300";
343
- formModalMinHeight = props?.editMode?.specs?.modalMinHeight;
344
- formModalMinWidth = props?.editMode?.specs?.modalMinWidth;
345
- formModalIcon = props?.editMode?.specs?.modalIcon || "window";
346
- formModalTitle = props?.editMode?.specs?.modalTitle || "Record Form";
347
- }
348
-
349
- const { Window: FormWindow, setWindowState: setFormWindowState } = useWindow({
350
- height: formModalHeight,
351
- minHeight: formModalMinHeight,
352
- minWidth: formModalMinWidth,
353
- onCloseCallBack: () => {
354
- props.apiActions.reloadData(props?.gridLoadParametersValues);
355
- },
356
- width: formModalWidth,
357
- windowIcon: formModalIcon,
358
- windowTitle: formModalTitle,
359
- });
360
-
361
- const handleCreateNewRecord = () => {
362
- if (props?.editMode?.editMode === "form") {
363
- navigate(props.editMode.specs.formRoute);
364
- } else if (props?.editMode?.editMode === "modal") {
365
- setRecordToEdit(null);
366
- setFormWindowState(true);
367
- } else if (props?.editMode?.editMode === "row") {
368
- currentNewRecordIndex = currentNewRecordIndex - 1;
369
- const newRecord: any = {};
370
- newRecord[keyColumnName] = currentNewRecordIndex;
371
- newRecord.isNew = true;
372
- for (const gridColumn of generatedColumns) {
373
- if (
374
- gridColumn?.type != "actions" &&
375
- gridColumn?.field != keyColumnName
376
- ) {
377
- if (gridColumn?.field) {
378
- newRecord[gridColumn.field] = null;
379
- }
380
- }
381
- }
382
- setData((oldRows: any) => [newRecord, ...oldRows]);
383
- setRowModesModel((oldModel: any) => ({
384
- ...oldModel,
385
- [currentNewRecordIndex]: { mode: GridRowModes.Edit },
386
- }));
387
- }
388
- };
389
-
390
- const handleEditRecord = async (record: any) => {
391
- if (record) {
392
- setRecordToEdit(record);
393
- if (props.editMode.editMode === "form") {
394
- navigate(props.editMode.specs.formRoute + "/" + record[keyColumnName]);
395
- } else if (props.editMode.editMode === "modal") {
396
- setFormWindowState(true);
397
- } else if (props?.editMode?.editMode === "row") {
398
- const id = record[keyColumnName];
399
- setRowModesModel({
400
- ...rowModesModel,
401
- [id]: { mode: GridRowModes.Edit },
402
- });
403
- }
404
- }
405
- };
406
-
407
- const validateRecord = (record: any) => {
408
- try {
409
- validationSchema.parse(record);
410
- } catch (err) {
411
- console.log("validateRecord err", err);
412
- let errorMessage: any = null;
413
- if (err instanceof z.ZodError) {
414
- errorMessage = err.errors
415
- .map(
416
- (error) => "Error in field (" + error.path + ") : " + error.message
417
- )
418
- .join(",");
419
- } else {
420
- errorMessage = "invalid record data";
421
- }
422
- return errorMessage;
423
- }
424
- };
425
-
426
- const handleSaveRowClick = (record: any) => {
427
- const id = record[keyColumnName];
428
- setRowModesModel({
429
- ...rowModesModel,
430
- [id]: { mode: GridRowModes.View },
431
- });
432
- };
433
-
434
- const processRowUpdate = async (record: any) => {
435
- if (props.editMode.editMode === "row") {
436
- let savedRecord: any = null;
437
- const errorMessage = validateRecord(record);
438
- if (errorMessage) {
439
- const errors = errorMessage.split(",");
440
- toast.error(
441
- <div style={{}}>
442
- {errors.map((error: any) => (
443
- <>
444
- <div>{error}</div>
445
- <Divider />
446
- </>
447
- ))}
448
- </div>
449
- );
450
- throw new Error(errorMessage);
451
- }
452
- if (props?.editAction?.preActionValidation) {
453
- if (!props.editAction.preActionValidation(record)) {
454
- throw new Error("error on the configured presave validation");
455
- }
456
- }
457
- const requestObject: any = { ...record };
458
- if (
459
- record[keyColumnName] &&
460
- isNumber(record[keyColumnName]) &&
461
- Number(record[keyColumnName]) < 0
462
- ) {
463
- requestObject[keyColumnName] = null;
464
- }
465
- savedRecord = await props.apiActions.saveRecord(requestObject);
466
- if (savedRecord == null) {
467
- throw new Error(
468
- "Failed to process your request, contact your administrator"
469
- );
470
- }
471
- if (props?.editAction?.postActionCallBack) {
472
- await props.editAction.postActionCallBack(record);
473
- }
474
- if (record?.isNew === true) {
475
- setData((oldData: any) => {
476
- const newData = oldData.filter(
477
- (x: any) => x[keyColumnName] !== record[keyColumnName]
478
- );
479
- return [savedRecord, ...newData];
480
- });
481
- }
482
- if (
483
- props?.editMode?.reloadAfterSave === true &&
484
- props?.apiActions?.reloadData
485
- ) {
486
- props?.apiActions?.reloadData(props?.gridLoadParametersValues);
487
- }
488
- return savedRecord;
489
- }
490
- };
491
-
492
- const handleCancelRowEditClick = (record: any) => {
493
- const id = record[keyColumnName];
494
- if (id && isNumber(id) && id < 0) {
495
- setRowModesModel({
496
- ...rowModesModel,
497
- [id]: { mode: GridRowModes.View, ignoreModifications: true },
498
- });
499
- setData((oldData: any) => {
500
- const newData = oldData.filter(
501
- (record: any) => record[keyColumnName] != id
502
- );
503
- return newData;
504
- });
505
- } else {
506
- setRowModesModel({
507
- ...rowModesModel,
508
- [id]: { mode: GridRowModes.View, ignoreModifications: true },
509
- });
510
- }
511
- };
512
-
513
- const getActionColumnActions = (params: GridRowParams<any>) => {
514
- const record: any = params.row;
515
- const actions: Array<React.ReactElement<GridActionsCellItemProps>> = [];
516
- if (props?.editMode?.editMode != "none") {
517
- if (
518
- props?.disableDefaultAction === undefined ||
519
- !props.disableDefaultAction
520
- ) {
521
- const isInEditMode: boolean =
522
- rowModesModel[record[keyColumnName]]?.mode === GridRowModes.Edit;
523
- if (props.editMode.editMode === "row" && isInEditMode) {
524
- if (isEditAllowed) {
525
- actions.push(
526
- <GridActionsCellItem
527
- icon={<FontAwesomeIcon icon="save" />}
528
- label={t("SAVE_BTN_LABEL")}
529
- onClick={() => {
530
- handleSaveRowClick(record);
531
- }}
532
- />
533
- );
534
- actions.push(
535
- <GridActionsCellItem
536
- icon={<FontAwesomeIcon icon="cancel" />}
537
- label={t("CANCEL_BTN_LABEL")}
538
- onClick={() => {
539
- handleCancelRowEditClick(record);
540
- }}
541
- color="inherit"
542
- />
543
- );
544
- }
545
- } else {
546
- if (props?.editAction && props?.editAction?.isEnabled === true) {
547
- let isEditActionVisibleForRecord = true;
548
- if (props?.editAction?.isActionVisibleForRecord) {
549
- isEditActionVisibleForRecord =
550
- props?.editAction?.isActionVisibleForRecord(record);
551
- }
552
- let isActionEditDisabledForRecord = false;
553
- if (props?.editAction?.isActionDisabledForRecord) {
554
- isActionEditDisabledForRecord =
555
- props?.editAction?.isActionDisabledForRecord(record);
556
- }
557
- if (isEditAllowed && isEditActionVisibleForRecord) {
558
- actions.push(
559
- <GridActionsCellItem
560
- disabled={isActionEditDisabledForRecord}
561
- icon={
562
- <Tooltip title={t("EDIT_BTN_LABEL")}>
563
- <FontAwesomeIcon
564
- icon={"edit"}
565
- style={{
566
- color: isActionEditDisabledForRecord
567
- ? "gray"
568
- : undefined,
569
- }}
570
- />
571
- </Tooltip>
572
- }
573
- showInMenu={
574
- props?.editAction?.gridActionProps?.showInMenu || false
575
- }
576
- label={t("EDIT_BTN_LABEL")}
577
- className="textPrimary"
578
- color="inherit"
579
- onClick={() => {
580
- if (isEditAllowed && !isActionEditDisabledForRecord) {
581
- handleEditRecord(record);
582
- }
583
- }}
584
- />
585
- );
586
- }
587
- }
588
- if (props?.deleteAction && props?.deleteAction?.isEnabled === true) {
589
- let isDeleteActionVisibleForRecord = true;
590
- if (props?.deleteAction?.isActionVisibleForRecord) {
591
- isDeleteActionVisibleForRecord =
592
- props?.deleteAction?.isActionVisibleForRecord(record);
593
- }
594
- let isDeleteActionDisabledForRecord = false;
595
- if (props?.deleteAction?.isActionDisabledForRecord) {
596
- isDeleteActionDisabledForRecord =
597
- props?.deleteAction?.isActionDisabledForRecord(record);
598
- }
599
- if (isDeleteAllowed && isDeleteActionVisibleForRecord) {
600
- actions.push(
601
- <GridActionsCellItem
602
- disabled={isDeleteActionDisabledForRecord}
603
- icon={
604
- <Tooltip title={t("DELETE_BTN_LABEL")}>
605
- <FontAwesomeIcon
606
- icon={"trash"}
607
- style={{
608
- color: isDeleteActionDisabledForRecord
609
- ? "gray"
610
- : undefined,
611
- }}
612
- />
613
- </Tooltip>
614
- }
615
- showInMenu={
616
- props?.deleteAction?.gridActionProps?.showInMenu || false
617
- }
618
- label={t("DELETE_BTN_LABEL")}
619
- className="textPrimary"
620
- color="inherit"
621
- onClick={() => {
622
- if (isDeleteAllowed && !isDeleteActionDisabledForRecord) {
623
- if (props?.deleteAction?.preActionValidation) {
624
- if (!props.deleteAction.preActionValidation(record)) {
625
- return;
626
- }
627
- }
628
- setRecordToDelete(record);
629
- setConfirmationWindowOpened(true);
630
- }
631
- }}
632
- />
633
- );
634
- }
635
- }
636
- }
637
- }
638
- }
639
- if (
640
- record[props?.keyColumnName || "id"] &&
641
- record[props?.keyColumnName || "id"] > 0 &&
642
- props?.attachment
643
- ) {
644
- actions?.push(
645
- <GridActionsCellItem
646
- icon={
647
- <Tooltip title={"Attachments"}>
648
- <FontAwesomeIcon icon={"paperclip"} />
649
- </Tooltip>
650
- }
651
- label={"Attachments"}
652
- className="textPrimary"
653
- color="inherit"
654
- onClick={() => {
655
- setSelectedRecord(record);
656
- if (props?.attachment?.enableAttachFn) {
657
- setAttachmentPanelEnabledForRecord(
658
- props.attachment.enableAttachFn(record)
659
- );
660
- } else {
661
- setAttachmentPanelEnabledForRecord(true);
662
- }
663
- setAttachmentWindowState(true);
664
- }}
665
- />
666
- );
667
- }
668
- if (
669
- record[props?.keyColumnName || "id"] &&
670
- record[props?.keyColumnName || "id"] > 0 &&
671
- props?.workFlowDocumentCode
672
- ) {
673
- actions?.push(
674
- <GridActionsCellItem
675
- icon={
676
- <Tooltip title={"Approvals"}>
677
- <FontAwesomeIcon icon={"stamp"} />
678
- </Tooltip>
679
- }
680
- label={"Approvals"}
681
- className="textPrimary"
682
- color="inherit"
683
- onClick={() => {
684
- setSelectedRecord(record);
685
- setWorkFlowWindowState(true);
686
- }}
687
- />
688
- );
689
- }
690
- if (
691
- record[props?.keyColumnName || "id"] &&
692
- record[props?.keyColumnName || "id"] > 0 &&
693
- props?.rowActions
694
- ) {
695
- for (const rowAction of props.rowActions) {
696
- if (
697
- !(
698
- rowAction?.gridActionProps?.multiRecord &&
699
- rowAction?.gridActionProps?.multiRecord === true
700
- )
701
- ) {
702
- actions.push(
703
- <TemplateGridRecordAction
704
- {...rowAction}
705
- record={record}
706
- reloadData={async () => {
707
- props.apiActions.reloadData(props?.gridLoadParametersValues);
708
- }}
709
- />
710
- );
711
- }
712
- }
713
- }
714
-
715
- return actions;
716
- };
717
-
718
- const actionColumn: TemplateGridColDef = {
719
- type: "actions",
720
- field: "actions",
721
- headerName: "",
722
- headerAlign: "center",
723
- width:
724
- (props?.rowActions ? props.rowActions.length * 30 : 0) +
725
- (props?.editAction && props?.editAction?.isEnabled ? 30 : 0) +
726
- (props?.deleteAction && props?.deleteAction?.isEnabled ? 30 : 0) +
727
- (props?.attachment ? 30 : 0) +
728
- (props?.workFlowDocumentCode ? 30 : 0),
729
- getActions: getActionColumnActions,
730
- };
731
-
732
- let structuredColumns: Array<TemplateGridColDef> = [];
733
- if (
734
- props?.disableDefaultAction === undefined ||
735
- !props.disableDefaultAction ||
736
- (props?.rowActions && props?.rowActions.length > 0)
737
- ) {
738
- structuredColumns.push(actionColumn);
739
- }
740
-
741
- structuredColumns = [...structuredColumns, ...generatedColumns];
742
- const handleRowSelection = (gridSelectionModel: GridRowSelectionModel) => {
743
- setRowSelectionModel(gridSelectionModel);
744
- };
745
- useEffect(() => {
746
- if (props?.autoLoad === undefined || props.autoLoad === true) {
747
- props.apiActions.reloadData(props?.gridLoadParametersValues);
748
- }
749
- adjustGridColumns();
750
- }, []);
751
-
752
- useEffect(() => {
753
- props.apiActions.reloadData(props?.gridLoadParametersValues);
754
- }, [session.UserInfo?.currentOrganization]);
755
-
756
- useEffect(() => {
757
- adjustGridColumns();
758
- }, [themeDirection, props.formElements]);
759
-
760
- const handleColumnVisibilityChange = (model: GridColumnVisibilityModel) => {
761
- setColumnVisibilityModel(model);
762
- if (props?.gridStateKey) {
763
- saveGridState(props.gridStateKey, { columnVisibilityModel: model });
764
- }
765
- };
766
-
767
- const handleColumnOrderChange: GridEventListener<"columnOrderChange"> = (
768
- params: GridColumnOrderChangeParams
769
- ) => {
770
- const { column, targetIndex } = params;
771
- setColumnOrder((prevOrder) => {
772
- const currentOrder = prevOrder.length
773
- ? [...prevOrder]
774
- : structuredColumns.map((col) => col.field);
775
- const fromIndex = currentOrder.indexOf(column.field);
776
- if (fromIndex === -1) return currentOrder;
777
-
778
- currentOrder.splice(fromIndex, 1); // remove
779
- currentOrder.splice(targetIndex, 0, column.field); // insert at new index
780
-
781
- if (props?.gridStateKey) {
782
- saveGridState(props.gridStateKey, { columnOrder: currentOrder });
783
- }
784
-
785
- return currentOrder;
786
- });
787
- };
788
-
789
- const handleRowGroupChange = (model: GridRowGroupingModel) => {
790
- setGridRowGroupingModel(model);
791
- if (props?.gridStateKey) {
792
- saveGridState(props.gridStateKey, { columnGroupingModel: model });
793
- }
794
- };
795
-
796
- // const handleColumnWidthChange: GridEventListener<"columnWidthChange"> = (
797
- // params: GridColumnResizeParams
798
- // ) => {
799
- // const updatedWidths = {
800
- // ...columnWidths,
801
- // [params.colDef.field]: params.width,
802
- // };
803
- // setColumnWidths(updatedWidths);
804
- // if (props?.gridStateKey) {
805
- // saveGridState(props.gridStateKey, { columnWidths: updatedWidths });
806
- // }
807
- // };
808
-
809
- const adjustedColumns: Array<TemplateGridColDef> = React.useMemo(() => {
810
- const baseCols = structuredColumns.map((col) => ({
811
- ...col,
812
- }));
813
-
814
- // Reorder based on saved columnOrder
815
- if (columnOrder.length) {
816
- const fieldToCol = new Map(baseCols.map((col) => [col.field, col]));
817
- return columnOrder.map((field) => fieldToCol.get(field)!).filter(Boolean);
818
- }
819
- return baseCols;
820
- }, [columnOrder, columnWidths, structuredColumns]);
821
-
822
- return (
823
- <>
824
- <ConfirmationWindow />
825
- {props?.editMode?.editMode === "modal" ? (
826
- <FormWindow>
827
- {props?.editMode?.specs?.formComponent ? (
828
- <props.editMode.specs.formComponent
829
- recordIdToEdit={
830
- recordToEdit ? recordToEdit[keyColumnName] : undefined
831
- }
832
- formCloseCallBk={() => {
833
- setFormWindowState(false);
834
- props.apiActions.reloadData(props?.gridLoadParametersValues);
835
- }}
836
- />
837
- ) : (
838
- <TemplateForm
839
- keyColumnName={props.keyColumnName}
840
- attachment={props.attachment}
841
- formValuesChangeCallBk={props?.formProps?.formValuesChangeCallBk}
842
- recordIdToEdit={
843
- recordToEdit ? recordToEdit[keyColumnName] : undefined
844
- }
845
- formCloseCallBk={() => {
846
- setFormWindowState(false);
847
- props.apiActions.reloadData(props?.gridLoadParametersValues);
848
- }}
849
- elements={props.formElements}
850
- apiActions={props.apiActions}
851
- editAuthorityKey={props?.editAction?.authority}
852
- formSavedSuccessfullyCallBk={
853
- props?.editAction?.postActionCallBack
854
- }
855
- preSaveValidation={props?.editAction?.preActionValidation}
856
- actions={props?.rowActions}
857
- />
858
- )}
859
- </FormWindow>
860
- ) : (
861
- <></>
862
- )}
863
- {props?.hideInfoBar === undefined && !props?.hideInfoBar ? (
864
- <Box sx={{ display: "flex" }}>
865
- {props?.hideBackButton === undefined && !props?.hideBackButton ? (
866
- <IconButton
867
- onClick={() => {
868
- navigate(-1, { replace: true });
869
- }}
870
- >
871
- <FontAwesomeIcon
872
- icon={themeDirection === "ltr" ? "arrow-left" : "arrow-right"}
873
- />
874
- </IconButton>
875
- ) : (
876
- <></>
877
- )}
878
- <Box
879
- sx={{
880
- flex: 1,
881
- display: "flex",
882
- alignItems: "center",
883
- justifyContent: "center",
884
- }}
885
- >
886
- {props?.girdIcon ? (
887
- <FontAwesomeIcon
888
- icon={props.girdIcon}
889
- style={{ marginRight: 5, marginLeft: 5 }}
890
- />
891
- ) : (
892
- <></>
893
- )}
894
- <Typography variant="h5">
895
- {props?.gridTitle
896
- ? capitalizeFirstLetter(t(props?.gridTitle))
897
- : ""}
898
- </Typography>
899
- </Box>
900
- </Box>
901
- ) : (
902
- <></>
903
- )}
904
-
905
- {props?.gridLoadParameters &&
906
- props?.gridLoadParameters.length > 0 &&
907
- props?.gridLoadParametersValues &&
908
- props?.setGridLoadParametersValues ? (
909
- <Accordion defaultExpanded sx={{ width: "100%" }}>
910
- <AccordionSummary expandIcon={<GridExpandMoreIcon />}>
911
- <Box
912
- sx={{
913
- display: "flex",
914
- alignItems: "center",
915
- justifyContent: "center",
916
- }}
917
- >
918
- <FontAwesomeIcon
919
- style={{ marginLeft: 5, marginRight: 5 }}
920
- icon="search"
921
- />
922
- <Typography component="span">Filters</Typography>
923
- </Box>
924
- </AccordionSummary>
925
- <AccordionDetails>
926
- <Box>
927
- <TemplateForm
928
- saveButtonSpecs={{
929
- label: t("SEARCH_BTN_LABEL"),
930
- icon: "search",
931
- actionButtonVariant: "outlined",
932
- actionButtonColor: "success",
933
- }}
934
- cancelButtonSpecs={{
935
- label: t("RESET_BTN_LABEL"),
936
- icon: "eraser",
937
- actionButtonVariant: "outlined",
938
- actionButtonColor: "error",
939
- }}
940
- apiActions={{
941
- deleteRecordById: async () => {
942
- return true;
943
- },
944
- saveRecord: async (params) => {
945
- if (params != undefined) {
946
- props.setGridLoadParametersValues(params);
947
- } else {
948
- props.setGridLoadParametersValues({});
949
- }
950
- props.apiActions.reloadData(params);
951
- },
952
- reloadData: async () => {},
953
- loadRecordById: async () => {},
954
- }}
955
- elements={props.gridLoadParameters}
956
- />
957
- </Box>
958
- </AccordionDetails>
959
- </Accordion>
960
- ) : (
961
- <></>
962
- )}
963
- {props?.attachment ? (
964
- <AttachmentWindow>
965
- <AttachmentPanel
966
- attachmentCode={props.attachment.attachmentCode}
967
- refKey={selectedRecord[props?.keyColumnName || "id"]}
968
- enableAttachment={attachmentPanelEnabledForRecord}
969
- />
970
- </AttachmentWindow>
971
- ) : (
972
- <></>
973
- )}
974
- {props?.workFlowDocumentCode ? (
975
- <WorkFlowWindow>
976
- <WorkflowDocumentPanel
977
- workFlowDocumentCode={props.workFlowDocumentCode}
978
- refDocumentId={selectedRecord[props?.keyColumnName || "id"]}
979
- postActionCallBk={() => {
980
- setWorkFlowWindowState(false);
981
- props.apiActions.reloadData(props.gridLoadParametersValues);
982
- }}
983
- cancelActionCallBk={() => {
984
- setWorkFlowWindowState(false);
985
- props.apiActions.reloadData(props.gridLoadParametersValues);
986
- }}
987
- />
988
- </WorkFlowWindow>
989
- ) : (
990
- <></>
991
- )}
992
- <DataGridPremium
993
- {...props?.muiProps}
994
- slots={{ toolbar: TemplateGridTopBar }}
995
- slotProps={{
996
- toolbar: {
997
- templateProps: {
998
- ...props,
999
- rowSelectionModel: rowSelectionModel,
1000
- data: props?.data,
1001
- },
1002
- handleCreateNewRecord,
1003
- clearGridState,
1004
- } as any,
1005
- }}
1006
- getRowId={(record: any) => {
1007
- return record[keyColumnName];
1008
- }}
1009
- showToolbar={true}
1010
- rows={props?.data}
1011
- columns={adjustedColumns}
1012
- checkboxSelection
1013
- editMode="row"
1014
- onRowEditStop={(params, event) => {
1015
- if (params.reason === "rowFocusOut") {
1016
- event.defaultMuiPrevented = true;
1017
- }
1018
- }}
1019
- rowModesModel={
1020
- props.editMode.editMode == "row" ? rowModesModel : undefined
1021
- }
1022
- onRowModesModelChange={
1023
- props.editMode.editMode == "row"
1024
- ? handleRowModesModelChange
1025
- : undefined
1026
- }
1027
- rowGroupingColumnMode="multiple"
1028
- processRowUpdate={processRowUpdate}
1029
- rowSelectionModel={rowSelectionModel}
1030
- onRowSelectionModelChange={handleRowSelection}
1031
- columnVisibilityModel={columnVisibilityModel}
1032
- onColumnVisibilityModelChange={handleColumnVisibilityChange}
1033
- onColumnOrderChange={handleColumnOrderChange}
1034
- // onColumnWidthChange={handleColumnWidthChange}
1035
- rowGroupingModel={gridRowGroupingModel}
1036
- onRowGroupingModelChange={(model: GridRowGroupingModel) => {
1037
- handleRowGroupChange(model);
1038
- }}
1039
- // pinnedColumns={pinnedColumns}
1040
- // onPinnedColumnsChange={handlePinnedColumnsChange}
1041
- sx={{ width: "100%" }}
1042
- />
1043
- </>
1044
- );
1045
- };
1046
-
1047
- export default TemplateGrid;
1
+ import {
2
+ DataGridPremium,
3
+ GridActionsCellItem,
4
+ GridActionsCellItemProps,
5
+ GridColumnOrderChangeParams,
6
+ GridColumnResizeParams,
7
+ GridColumnVisibilityModel,
8
+ GridEventListener,
9
+ GridExpandMoreIcon,
10
+ GridPinnedColumnFields,
11
+ GridRowGroupingModel,
12
+ GridRowModes,
13
+ GridRowModesModel,
14
+ GridRowParams,
15
+ GridRowSelectionModel,
16
+ } from "@mui/x-data-grid-premium";
17
+ import { TemplateGridColDef, TemplateGridProps } from "../DataEntryTypes";
18
+ import TemplateGridTopBar from "./TemplateGridTopBar";
19
+ import { useNavigate } from "react-router-dom";
20
+ import React, { useEffect, useState } from "react";
21
+ import { z } from "zod";
22
+ import {
23
+ constructGridColumnsFromFields,
24
+ constructValidationSchema,
25
+ getAllFields,
26
+ } from "../DataEntryUtil";
27
+ import { toast } from "react-toastify";
28
+ import {
29
+ Accordion,
30
+ AccordionDetails,
31
+ AccordionSummary,
32
+ Box,
33
+ Divider,
34
+ IconButton,
35
+ Tooltip,
36
+ Typography,
37
+ } from "@mui/material";
38
+ import { capitalizeFirstLetter, isNumber } from "../../../../util/AppUtils";
39
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
40
+ import TemplateGridRecordAction from "./TemplateGridRecordAction";
41
+ import TemplateForm from "../TemplateDataForm/TemplateForm";
42
+ import { useSelector } from "react-redux";
43
+ import { useTranslation } from "react-i18next";
44
+ import useSession from "../../../../hooks/UseSession";
45
+ import { useConfirmationWindow } from "../../../../hooks/UseConfirmationWindow";
46
+ import { useWindow } from "../../../../hooks/UseWindow";
47
+ import { useAxios } from "../../../../hooks";
48
+ import useLookupGridColumn from "../../../../hooks/useLookupGridColumn";
49
+ import { ContinuousColorLegend } from "@mui/x-charts";
50
+ import AttachmentPanel from "../../attachment/AttachmentPanel";
51
+ import WorkflowDocumentPanel from "../../workflow/WorkflowDocumentPanel";
52
+ let currentNewRecordIndex = -1;
53
+
54
+ interface GridState {
55
+ columnVisibilityModel?: GridColumnVisibilityModel;
56
+ columnOrder?: string[];
57
+ columnWidths?: { [key: string]: number };
58
+ columnGroupingModel?: GridRowGroupingModel;
59
+ pinnedColumns?: GridPinnedColumnFields;
60
+ }
61
+
62
+ const loadGridState = (gridStateKey: string): GridState => {
63
+ try {
64
+ const saved = localStorage.getItem(gridStateKey);
65
+ return saved ? JSON.parse(saved) : {};
66
+ } catch (e) {
67
+ console.error("Failed to load grid state", e);
68
+ return {};
69
+ }
70
+ };
71
+
72
+ const saveGridState = (gridStateKey: string, statePart: GridState) => {
73
+ const current = loadGridState(gridStateKey);
74
+ localStorage.setItem(
75
+ gridStateKey,
76
+ JSON.stringify({ ...current, ...statePart })
77
+ );
78
+ };
79
+
80
+ const PIN_FIXED_COLUMNS = ["__check__", "actions"];
81
+
82
+ const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
83
+ const { t } = useTranslation();
84
+ const { handleGetRequest } = useAxios();
85
+ const stores = useSelector((state: any) => state.commonStores.stores);
86
+ const AppLayout = useSelector((state: any) => state.AppLayout);
87
+ const [selectedRecord, setSelectedRecord] = useState<any>({});
88
+ const [attachmentPanelEnabledForRecord, setAttachmentPanelEnabledForRecord] =
89
+ useState<boolean>(true);
90
+ const { getLookupOptions } = useLookupGridColumn();
91
+ const { Window: AttachmentWindow, setWindowState: setAttachmentWindowState } =
92
+ useWindow({
93
+ windowTitle: t(props.gridTitle) + " Attachments",
94
+ windowIcon: "paperclip",
95
+ width: "fit-content",
96
+ height: "fit-content",
97
+ minHeight: 500,
98
+ minWidth: "50%",
99
+ onCloseCallBack: () => {
100
+ props?.apiActions?.reloadData(props.gridLoadParametersValues);
101
+ },
102
+ });
103
+ const { Window: WorkFlowWindow, setWindowState: setWorkFlowWindowState } =
104
+ useWindow({
105
+ windowTitle: t(props.gridTitle) + " Approvals",
106
+ windowIcon: "stamp",
107
+ height: "fit-content",
108
+ minHeight: 500,
109
+ width: "fit-content",
110
+ // width:1100,
111
+ onCloseCallBack: () => {
112
+ props?.apiActions?.reloadData(props.gridLoadParametersValues);
113
+ },
114
+ });
115
+ const [generatedColumns, setGeneratedColumns] = useState<
116
+ Array<TemplateGridColDef>
117
+ >([]);
118
+ const fields = getAllFields(props.formElements);
119
+ const hiddenFields = [];
120
+ const savedState = React.useMemo<GridState>(
121
+ () => (props?.gridStateKey ? loadGridState(props.gridStateKey) : {}),
122
+ []
123
+ );
124
+ for (const field of fields) {
125
+ if (field?.gridProps?.hidden === true) {
126
+ hiddenFields.push(field.fieldName);
127
+ }
128
+ }
129
+ let initialVisibilityState: GridColumnVisibilityModel = {};
130
+ if (savedState?.columnVisibilityModel) {
131
+ initialVisibilityState = savedState.columnVisibilityModel;
132
+ const existingFields = Object.keys(initialVisibilityState);
133
+ for (const field of hiddenFields) {
134
+ if (!existingFields.includes(field)) {
135
+ initialVisibilityState[field] = false;
136
+ }
137
+ }
138
+ } else {
139
+ for (const field of hiddenFields) {
140
+ initialVisibilityState[field] = false;
141
+ }
142
+ }
143
+
144
+ const themeDirection = useSelector(
145
+ (state: any) => state.AppLayout.appDirection
146
+ );
147
+
148
+ const clearGridState = () => {
149
+ if (props?.gridStateKey) {
150
+ localStorage.removeItem(props.gridStateKey);
151
+ }
152
+ setColumnVisibilityModel({});
153
+ setColumnOrder([]);
154
+ setColumnWidths({});
155
+ setGridRowGroupingModel([]);
156
+ setPinnedColumns({
157
+ left: [...(themeDirection === "ltr" ? PIN_FIXED_COLUMNS : [])],
158
+ right: [...(themeDirection === "rtl" ? PIN_FIXED_COLUMNS : [])],
159
+ });
160
+ };
161
+ const [columnVisibilityModel, setColumnVisibilityModel] =
162
+ React.useState<GridColumnVisibilityModel>(initialVisibilityState);
163
+ const [columnOrder, setColumnOrder] = React.useState<string[]>(
164
+ savedState.columnOrder || []
165
+ );
166
+ const [columnWidths, setColumnWidths] = React.useState<{
167
+ [key: string]: number;
168
+ }>(savedState.columnWidths || {});
169
+ const [gridRowGroupingModel, setGridRowGroupingModel] =
170
+ useState<GridRowGroupingModel>(savedState.columnGroupingModel || []);
171
+ let newLeft = savedState?.pinnedColumns?.left || [];
172
+ newLeft = newLeft.filter(
173
+ (record: any) => !PIN_FIXED_COLUMNS.includes(record)
174
+ );
175
+ let newRight = savedState?.pinnedColumns?.right || [] || [];
176
+ newRight = newRight.filter(
177
+ (record: any) => !PIN_FIXED_COLUMNS.includes(record)
178
+ );
179
+
180
+ const [pinnedColumns, setPinnedColumns] = useState<GridPinnedColumnFields>({
181
+ left: [...(themeDirection === "ltr" ? PIN_FIXED_COLUMNS : []), ...newLeft],
182
+ right: [
183
+ ...(themeDirection === "rtl" ? PIN_FIXED_COLUMNS : []),
184
+ ...newRight,
185
+ ],
186
+ });
187
+
188
+ const session = useSession();
189
+ const navigate = useNavigate();
190
+ const [recordToDelete, setRecordToDelete] = useState<any>(null);
191
+ const [recordToEdit, setRecordToEdit] = useState<any>(null);
192
+ const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
193
+ const loadComboboxData: (
194
+ storeUrl: any,
195
+ storeLoadParam: any,
196
+ dataQueryId: any
197
+ ) => Promise<Array<any>> = async (storeUrl, storeLoadParam, dataQueryId) => {
198
+ let comboboxData: Array<any> = [];
199
+ if (storeUrl) {
200
+ await handleGetRequest({
201
+ endPointURI: storeUrl,
202
+ showMask: false,
203
+ parameters: storeLoadParam,
204
+ successCallBkFn: (response) => {
205
+ comboboxData = response.data;
206
+ },
207
+ });
208
+ } else if (dataQueryId) {
209
+ await handleGetRequest({
210
+ endPointURI: "api/v1/dev/query/result",
211
+ showMask: false,
212
+ parameters: { queryId: dataQueryId, ...storeLoadParam },
213
+ successCallBkFn: (response) => {
214
+ comboboxData = response.data;
215
+ },
216
+ });
217
+ }
218
+ return comboboxData;
219
+ };
220
+ const adjustGridColumns = async () => {
221
+ let gridColumns = constructGridColumnsFromFields(
222
+ fields,
223
+ props?.editMode?.editMode === "row" || false,
224
+ t
225
+ );
226
+ for (let gridColumn of gridColumns) {
227
+ if (gridColumn?.lookupType) {
228
+ gridColumn.displayField =
229
+ AppLayout.appDirection === "ltr"
230
+ ? "lookupEnDisplay"
231
+ : "lookupArDisplay";
232
+ gridColumn.options = await getLookupOptions(gridColumn.lookupType);
233
+ gridColumn.valueField = "lookupValue";
234
+ }
235
+ if (gridColumn.type === "custom") {
236
+ debugger;
237
+ if (gridColumn?.options) {
238
+ continue;
239
+ } else if (gridColumn?.commonStoreKey) {
240
+ gridColumn.options = stores[gridColumn.commonStoreKey]?.data || [];
241
+ } else {
242
+ gridColumn.options = await loadComboboxData(
243
+ gridColumn?.storeUrl,
244
+ gridColumn?.storeLoadParam,
245
+ gridColumn?.dataQueryId
246
+ );
247
+ }
248
+ }
249
+ }
250
+ setGeneratedColumns(gridColumns);
251
+ };
252
+ const [rowSelectionModel, setRowSelectionModel] =
253
+ useState<GridRowSelectionModel>({ ids: new Set(), type: "include" });
254
+ let keyColumnName: string = "id";
255
+ if (props?.keyColumnName) {
256
+ keyColumnName = props?.keyColumnName;
257
+ }
258
+ const setData = props.setData;
259
+ let validationSchema = null;
260
+ if (props?.validationSchema) {
261
+ validationSchema = props.validationSchema;
262
+ } else {
263
+ validationSchema = z.object(constructValidationSchema(fields));
264
+ }
265
+
266
+ let isEditAllowed = true;
267
+ if (props?.editAction?.authority) {
268
+ isEditAllowed = session.isUserAuthorized(props.editAction.authority);
269
+ }
270
+ let isDeleteAllowed = true;
271
+ if (props?.deleteAction?.authority) {
272
+ isDeleteAllowed = session.isUserAuthorized(props.deleteAction.authority);
273
+ }
274
+
275
+ const handleRowModesModelChange = (newModel: GridRowModesModel) => {
276
+ setRowModesModel(newModel);
277
+ };
278
+
279
+ const handlePinnedColumnsChange = (newModel: GridPinnedColumnFields) => {
280
+ let newLeft = newModel?.left || [];
281
+ newLeft = newLeft.filter(
282
+ (record: any) => !PIN_FIXED_COLUMNS.includes(record)
283
+ );
284
+ let newRight = newModel?.right || [];
285
+ newRight = newRight.filter(
286
+ (record: any) => !PIN_FIXED_COLUMNS.includes(record)
287
+ );
288
+ const newPinedColumns: GridPinnedColumnFields = {
289
+ left: [
290
+ ...(themeDirection === "ltr" ? PIN_FIXED_COLUMNS : []),
291
+ ...newLeft,
292
+ ],
293
+ right: [
294
+ ...(themeDirection === "rtl" ? PIN_FIXED_COLUMNS : []),
295
+ ...newRight,
296
+ ],
297
+ };
298
+ setPinnedColumns(newPinedColumns);
299
+ if (props?.gridStateKey) {
300
+ saveGridState(props.gridStateKey, { pinnedColumns: newPinedColumns });
301
+ }
302
+ };
303
+
304
+ const handleDeleteRecord = async () => {
305
+ let result = true;
306
+ if (!(recordToDelete && recordToDelete?.isNew === true)) {
307
+ result = await props.apiActions.deleteRecordById(
308
+ recordToDelete[keyColumnName]
309
+ );
310
+ }
311
+ if (result) {
312
+ if (props?.deleteAction?.postActionCallBack) {
313
+ await props.deleteAction.postActionCallBack(recordToDelete);
314
+ }
315
+
316
+ setData((oldData: any) => {
317
+ const newData = oldData.filter(
318
+ (record: any) =>
319
+ record[keyColumnName] != recordToDelete[keyColumnName]
320
+ );
321
+ return newData;
322
+ });
323
+ }
324
+ };
325
+
326
+ const { ConfirmationWindow, setOpen: setConfirmationWindowOpened } =
327
+ useConfirmationWindow({
328
+ title: "Confirmation",
329
+ body: "Are you sure you want to delete this record ?",
330
+ onConfirmationCallBk: handleDeleteRecord,
331
+ });
332
+
333
+ let formModalHeight = undefined;
334
+ let formModalWidth = undefined;
335
+ let formModalMinHeight = undefined;
336
+ let formModalMinWidth = undefined;
337
+ let formModalIcon = undefined;
338
+ let formModalTitle = undefined;
339
+
340
+ if (props?.editMode?.editMode === "modal") {
341
+ formModalHeight = props?.editMode?.specs?.modalHeight || "fit-content";
342
+ formModalWidth = props?.editMode?.specs?.modalWidth || "300";
343
+ formModalMinHeight = props?.editMode?.specs?.modalMinHeight;
344
+ formModalMinWidth = props?.editMode?.specs?.modalMinWidth;
345
+ formModalIcon = props?.editMode?.specs?.modalIcon || "window";
346
+ formModalTitle = props?.editMode?.specs?.modalTitle || "Record Form";
347
+ }
348
+
349
+ const { Window: FormWindow, setWindowState: setFormWindowState } = useWindow({
350
+ height: formModalHeight,
351
+ minHeight: formModalMinHeight,
352
+ minWidth: formModalMinWidth,
353
+ onCloseCallBack: () => {
354
+ props.apiActions.reloadData(props?.gridLoadParametersValues);
355
+ },
356
+ width: formModalWidth,
357
+ windowIcon: formModalIcon,
358
+ windowTitle: formModalTitle,
359
+ });
360
+
361
+ const handleCreateNewRecord = () => {
362
+ if (props?.editMode?.editMode === "form") {
363
+ navigate(props.editMode.specs.formRoute);
364
+ } else if (props?.editMode?.editMode === "modal") {
365
+ setRecordToEdit(null);
366
+ setFormWindowState(true);
367
+ } else if (props?.editMode?.editMode === "row") {
368
+ currentNewRecordIndex = currentNewRecordIndex - 1;
369
+ const newRecord: any = {};
370
+ newRecord[keyColumnName] = currentNewRecordIndex;
371
+ newRecord.isNew = true;
372
+ for (const gridColumn of generatedColumns) {
373
+ if (
374
+ gridColumn?.type != "actions" &&
375
+ gridColumn?.field != keyColumnName
376
+ ) {
377
+ if (gridColumn?.field) {
378
+ newRecord[gridColumn.field] = null;
379
+ }
380
+ }
381
+ }
382
+ setData((oldRows: any) => [newRecord, ...oldRows]);
383
+ setRowModesModel((oldModel: any) => ({
384
+ ...oldModel,
385
+ [currentNewRecordIndex]: { mode: GridRowModes.Edit },
386
+ }));
387
+ }
388
+ };
389
+
390
+ const handleEditRecord = async (record: any) => {
391
+ if (record) {
392
+ setRecordToEdit(record);
393
+ if (props.editMode.editMode === "form") {
394
+ navigate(props.editMode.specs.formRoute + "/" + record[keyColumnName]);
395
+ } else if (props.editMode.editMode === "modal") {
396
+ setFormWindowState(true);
397
+ } else if (props?.editMode?.editMode === "row") {
398
+ const id = record[keyColumnName];
399
+ setRowModesModel({
400
+ ...rowModesModel,
401
+ [id]: { mode: GridRowModes.Edit },
402
+ });
403
+ }
404
+ }
405
+ };
406
+
407
+ const validateRecord = (record: any) => {
408
+ try {
409
+ validationSchema.parse(record);
410
+ } catch (err) {
411
+ console.log("validateRecord err", err);
412
+ let errorMessage: any = null;
413
+ if (err instanceof z.ZodError) {
414
+ errorMessage = err.errors
415
+ .map(
416
+ (error) => "Error in field (" + error.path + ") : " + error.message
417
+ )
418
+ .join(",");
419
+ } else {
420
+ errorMessage = "invalid record data";
421
+ }
422
+ return errorMessage;
423
+ }
424
+ };
425
+
426
+ const handleSaveRowClick = (record: any) => {
427
+ const id = record[keyColumnName];
428
+ setRowModesModel({
429
+ ...rowModesModel,
430
+ [id]: { mode: GridRowModes.View },
431
+ });
432
+ };
433
+
434
+ const processRowUpdate = async (record: any) => {
435
+ if (props.editMode.editMode === "row") {
436
+ let savedRecord: any = null;
437
+ const errorMessage = validateRecord(record);
438
+ if (errorMessage) {
439
+ const errors = errorMessage.split(",");
440
+ toast.error(
441
+ <div style={{}}>
442
+ {errors.map((error: any) => (
443
+ <>
444
+ <div>{error}</div>
445
+ <Divider />
446
+ </>
447
+ ))}
448
+ </div>
449
+ );
450
+ throw new Error(errorMessage);
451
+ }
452
+ if (props?.editAction?.preActionValidation) {
453
+ if (!props.editAction.preActionValidation(record)) {
454
+ throw new Error("error on the configured presave validation");
455
+ }
456
+ }
457
+ const requestObject: any = { ...record };
458
+ if (
459
+ record[keyColumnName] &&
460
+ isNumber(record[keyColumnName]) &&
461
+ Number(record[keyColumnName]) < 0
462
+ ) {
463
+ requestObject[keyColumnName] = null;
464
+ }
465
+ savedRecord = await props.apiActions.saveRecord(requestObject);
466
+ if (savedRecord == null) {
467
+ throw new Error(
468
+ "Failed to process your request, contact your administrator"
469
+ );
470
+ }
471
+ if (props?.editAction?.postActionCallBack) {
472
+ await props.editAction.postActionCallBack(record);
473
+ }
474
+ if (record?.isNew === true) {
475
+ setData((oldData: any) => {
476
+ const newData = oldData.filter(
477
+ (x: any) => x[keyColumnName] !== record[keyColumnName]
478
+ );
479
+ return [savedRecord, ...newData];
480
+ });
481
+ }
482
+ if (
483
+ props?.editMode?.reloadAfterSave === true &&
484
+ props?.apiActions?.reloadData
485
+ ) {
486
+ props?.apiActions?.reloadData(props?.gridLoadParametersValues);
487
+ }
488
+ return savedRecord;
489
+ }
490
+ };
491
+
492
+ const handleCancelRowEditClick = (record: any) => {
493
+ const id = record[keyColumnName];
494
+ if (id && isNumber(id) && id < 0) {
495
+ setRowModesModel({
496
+ ...rowModesModel,
497
+ [id]: { mode: GridRowModes.View, ignoreModifications: true },
498
+ });
499
+ setData((oldData: any) => {
500
+ const newData = oldData.filter(
501
+ (record: any) => record[keyColumnName] != id
502
+ );
503
+ return newData;
504
+ });
505
+ } else {
506
+ setRowModesModel({
507
+ ...rowModesModel,
508
+ [id]: { mode: GridRowModes.View, ignoreModifications: true },
509
+ });
510
+ }
511
+ };
512
+
513
+ const getActionColumnActions = (params: GridRowParams<any>) => {
514
+ const record: any = params.row;
515
+ const actions: Array<React.ReactElement<GridActionsCellItemProps>> = [];
516
+ if (props?.editMode?.editMode != "none") {
517
+ if (
518
+ props?.disableDefaultAction === undefined ||
519
+ !props.disableDefaultAction
520
+ ) {
521
+ const isInEditMode: boolean =
522
+ rowModesModel[record[keyColumnName]]?.mode === GridRowModes.Edit;
523
+ if (props.editMode.editMode === "row" && isInEditMode) {
524
+ if (isEditAllowed) {
525
+ actions.push(
526
+ <GridActionsCellItem
527
+ icon={<FontAwesomeIcon icon="save" />}
528
+ label={t("SAVE_BTN_LABEL")}
529
+ onClick={() => {
530
+ handleSaveRowClick(record);
531
+ }}
532
+ />
533
+ );
534
+ actions.push(
535
+ <GridActionsCellItem
536
+ icon={<FontAwesomeIcon icon="cancel" />}
537
+ label={t("CANCEL_BTN_LABEL")}
538
+ onClick={() => {
539
+ handleCancelRowEditClick(record);
540
+ }}
541
+ color="inherit"
542
+ />
543
+ );
544
+ }
545
+ } else {
546
+ if (props?.editAction && props?.editAction?.isEnabled === true) {
547
+ let isEditActionVisibleForRecord = true;
548
+ if (props?.editAction?.isActionVisibleForRecord) {
549
+ isEditActionVisibleForRecord =
550
+ props?.editAction?.isActionVisibleForRecord(record);
551
+ }
552
+ let isActionEditDisabledForRecord = false;
553
+ if (props?.editAction?.isActionDisabledForRecord) {
554
+ isActionEditDisabledForRecord =
555
+ props?.editAction?.isActionDisabledForRecord(record);
556
+ }
557
+ if (isEditAllowed && isEditActionVisibleForRecord) {
558
+ actions.push(
559
+ <GridActionsCellItem
560
+ disabled={isActionEditDisabledForRecord}
561
+ icon={
562
+ <Tooltip title={t("EDIT_BTN_LABEL")}>
563
+ <FontAwesomeIcon
564
+ icon={"edit"}
565
+ style={{
566
+ color: isActionEditDisabledForRecord
567
+ ? "gray"
568
+ : undefined,
569
+ }}
570
+ />
571
+ </Tooltip>
572
+ }
573
+ showInMenu={
574
+ props?.editAction?.gridActionProps?.showInMenu || false
575
+ }
576
+ label={t("EDIT_BTN_LABEL")}
577
+ className="textPrimary"
578
+ color="inherit"
579
+ onClick={() => {
580
+ if (isEditAllowed && !isActionEditDisabledForRecord) {
581
+ handleEditRecord(record);
582
+ }
583
+ }}
584
+ />
585
+ );
586
+ }
587
+ }
588
+ if (props?.deleteAction && props?.deleteAction?.isEnabled === true) {
589
+ let isDeleteActionVisibleForRecord = true;
590
+ if (props?.deleteAction?.isActionVisibleForRecord) {
591
+ isDeleteActionVisibleForRecord =
592
+ props?.deleteAction?.isActionVisibleForRecord(record);
593
+ }
594
+ let isDeleteActionDisabledForRecord = false;
595
+ if (props?.deleteAction?.isActionDisabledForRecord) {
596
+ isDeleteActionDisabledForRecord =
597
+ props?.deleteAction?.isActionDisabledForRecord(record);
598
+ }
599
+ if (isDeleteAllowed && isDeleteActionVisibleForRecord) {
600
+ actions.push(
601
+ <GridActionsCellItem
602
+ disabled={isDeleteActionDisabledForRecord}
603
+ icon={
604
+ <Tooltip title={t("DELETE_BTN_LABEL")}>
605
+ <FontAwesomeIcon
606
+ icon={"trash"}
607
+ style={{
608
+ color: isDeleteActionDisabledForRecord
609
+ ? "gray"
610
+ : undefined,
611
+ }}
612
+ />
613
+ </Tooltip>
614
+ }
615
+ showInMenu={
616
+ props?.deleteAction?.gridActionProps?.showInMenu || false
617
+ }
618
+ label={t("DELETE_BTN_LABEL")}
619
+ className="textPrimary"
620
+ color="inherit"
621
+ onClick={() => {
622
+ if (isDeleteAllowed && !isDeleteActionDisabledForRecord) {
623
+ if (props?.deleteAction?.preActionValidation) {
624
+ if (!props.deleteAction.preActionValidation(record)) {
625
+ return;
626
+ }
627
+ }
628
+ setRecordToDelete(record);
629
+ setConfirmationWindowOpened(true);
630
+ }
631
+ }}
632
+ />
633
+ );
634
+ }
635
+ }
636
+ }
637
+ }
638
+ }
639
+ if (
640
+ record[props?.keyColumnName || "id"] &&
641
+ record[props?.keyColumnName || "id"] > 0 &&
642
+ props?.attachment
643
+ ) {
644
+ actions?.push(
645
+ <GridActionsCellItem
646
+ icon={
647
+ <Tooltip title={"Attachments"}>
648
+ <FontAwesomeIcon icon={"paperclip"} />
649
+ </Tooltip>
650
+ }
651
+ label={"Attachments"}
652
+ className="textPrimary"
653
+ color="inherit"
654
+ onClick={() => {
655
+ setSelectedRecord(record);
656
+ if (props?.attachment?.enableAttachFn) {
657
+ setAttachmentPanelEnabledForRecord(
658
+ props.attachment.enableAttachFn(record)
659
+ );
660
+ } else {
661
+ setAttachmentPanelEnabledForRecord(true);
662
+ }
663
+ setAttachmentWindowState(true);
664
+ }}
665
+ />
666
+ );
667
+ }
668
+ if (
669
+ record[props?.keyColumnName || "id"] &&
670
+ record[props?.keyColumnName || "id"] > 0 &&
671
+ props?.workFlowDocumentCode
672
+ ) {
673
+ actions?.push(
674
+ <GridActionsCellItem
675
+ icon={
676
+ <Tooltip title={"Approvals"}>
677
+ <FontAwesomeIcon icon={"stamp"} />
678
+ </Tooltip>
679
+ }
680
+ label={"Approvals"}
681
+ className="textPrimary"
682
+ color="inherit"
683
+ onClick={() => {
684
+ setSelectedRecord(record);
685
+ setWorkFlowWindowState(true);
686
+ }}
687
+ />
688
+ );
689
+ }
690
+ if (
691
+ record[props?.keyColumnName || "id"] &&
692
+ record[props?.keyColumnName || "id"] > 0 &&
693
+ props?.rowActions
694
+ ) {
695
+ for (const rowAction of props.rowActions) {
696
+ if (
697
+ !(
698
+ rowAction?.gridActionProps?.multiRecord &&
699
+ rowAction?.gridActionProps?.multiRecord === true
700
+ )
701
+ ) {
702
+ actions.push(
703
+ <TemplateGridRecordAction
704
+ {...rowAction}
705
+ record={record}
706
+ reloadData={async () => {
707
+ props.apiActions.reloadData(props?.gridLoadParametersValues);
708
+ }}
709
+ />
710
+ );
711
+ }
712
+ }
713
+ }
714
+
715
+ return actions;
716
+ };
717
+
718
+ const actionColumn: TemplateGridColDef = {
719
+ type: "actions",
720
+ field: "actions",
721
+ headerName: "",
722
+ headerAlign: "center",
723
+ width:
724
+ (props?.rowActions ? props.rowActions.length * 30 : 0) +
725
+ (props?.editAction && props?.editAction?.isEnabled ? 30 : 0) +
726
+ (props?.deleteAction && props?.deleteAction?.isEnabled ? 30 : 0) +
727
+ (props?.attachment ? 30 : 0) +
728
+ (props?.workFlowDocumentCode ? 30 : 0),
729
+ getActions: getActionColumnActions,
730
+ };
731
+
732
+ let structuredColumns: Array<TemplateGridColDef> = [];
733
+ if (
734
+ props?.disableDefaultAction === undefined ||
735
+ !props.disableDefaultAction ||
736
+ (props?.rowActions && props?.rowActions.length > 0)
737
+ ) {
738
+ structuredColumns.push(actionColumn);
739
+ }
740
+
741
+ structuredColumns = [...structuredColumns, ...generatedColumns];
742
+ const handleRowSelection = (gridSelectionModel: GridRowSelectionModel) => {
743
+ setRowSelectionModel(gridSelectionModel);
744
+ };
745
+ useEffect(() => {
746
+ if (props?.autoLoad === undefined || props.autoLoad === true) {
747
+ props.apiActions.reloadData(props?.gridLoadParametersValues);
748
+ }
749
+ adjustGridColumns();
750
+ }, []);
751
+
752
+ useEffect(() => {
753
+ props.apiActions.reloadData(props?.gridLoadParametersValues);
754
+ }, [session.UserInfo?.currentOrganization]);
755
+
756
+ useEffect(() => {
757
+ adjustGridColumns();
758
+ }, [themeDirection, props.formElements]);
759
+
760
+ const handleColumnVisibilityChange = (model: GridColumnVisibilityModel) => {
761
+ setColumnVisibilityModel(model);
762
+ if (props?.gridStateKey) {
763
+ saveGridState(props.gridStateKey, { columnVisibilityModel: model });
764
+ }
765
+ };
766
+
767
+ const handleColumnOrderChange: GridEventListener<"columnOrderChange"> = (
768
+ params: GridColumnOrderChangeParams
769
+ ) => {
770
+ const { column, targetIndex } = params;
771
+ setColumnOrder((prevOrder) => {
772
+ const currentOrder = prevOrder.length
773
+ ? [...prevOrder]
774
+ : structuredColumns.map((col) => col.field);
775
+ const fromIndex = currentOrder.indexOf(column.field);
776
+ if (fromIndex === -1) return currentOrder;
777
+
778
+ currentOrder.splice(fromIndex, 1); // remove
779
+ currentOrder.splice(targetIndex, 0, column.field); // insert at new index
780
+
781
+ if (props?.gridStateKey) {
782
+ saveGridState(props.gridStateKey, { columnOrder: currentOrder });
783
+ }
784
+
785
+ return currentOrder;
786
+ });
787
+ };
788
+
789
+ const handleRowGroupChange = (model: GridRowGroupingModel) => {
790
+ setGridRowGroupingModel(model);
791
+ if (props?.gridStateKey) {
792
+ saveGridState(props.gridStateKey, { columnGroupingModel: model });
793
+ }
794
+ };
795
+
796
+ // const handleColumnWidthChange: GridEventListener<"columnWidthChange"> = (
797
+ // params: GridColumnResizeParams
798
+ // ) => {
799
+ // const updatedWidths = {
800
+ // ...columnWidths,
801
+ // [params.colDef.field]: params.width,
802
+ // };
803
+ // setColumnWidths(updatedWidths);
804
+ // if (props?.gridStateKey) {
805
+ // saveGridState(props.gridStateKey, { columnWidths: updatedWidths });
806
+ // }
807
+ // };
808
+
809
+ const adjustedColumns: Array<TemplateGridColDef> = React.useMemo(() => {
810
+ const baseCols = structuredColumns.map((col) => ({
811
+ ...col,
812
+ }));
813
+
814
+ // Reorder based on saved columnOrder
815
+ if (columnOrder.length) {
816
+ const fieldToCol = new Map(baseCols.map((col) => [col.field, col]));
817
+ return columnOrder.map((field) => fieldToCol.get(field)!).filter(Boolean);
818
+ }
819
+ return baseCols;
820
+ }, [columnOrder, columnWidths, structuredColumns]);
821
+
822
+ return (
823
+ <>
824
+ <ConfirmationWindow />
825
+ {props?.editMode?.editMode === "modal" ? (
826
+ <FormWindow>
827
+ {props?.editMode?.specs?.formComponent ? (
828
+ <props.editMode.specs.formComponent
829
+ recordIdToEdit={
830
+ recordToEdit ? recordToEdit[keyColumnName] : undefined
831
+ }
832
+ formCloseCallBk={() => {
833
+ setFormWindowState(false);
834
+ props.apiActions.reloadData(props?.gridLoadParametersValues);
835
+ }}
836
+ />
837
+ ) : (
838
+ <TemplateForm
839
+ keyColumnName={props.keyColumnName}
840
+ attachment={props.attachment}
841
+ formValuesChangeCallBk={props?.formProps?.formValuesChangeCallBk}
842
+ recordIdToEdit={
843
+ recordToEdit ? recordToEdit[keyColumnName] : undefined
844
+ }
845
+ formCloseCallBk={() => {
846
+ setFormWindowState(false);
847
+ props.apiActions.reloadData(props?.gridLoadParametersValues);
848
+ }}
849
+ elements={props.formElements}
850
+ apiActions={props.apiActions}
851
+ editAuthorityKey={props?.editAction?.authority}
852
+ formSavedSuccessfullyCallBk={
853
+ props?.editAction?.postActionCallBack
854
+ }
855
+ preSaveValidation={props?.editAction?.preActionValidation}
856
+ actions={props?.rowActions}
857
+ />
858
+ )}
859
+ </FormWindow>
860
+ ) : (
861
+ <></>
862
+ )}
863
+ {props?.hideInfoBar === undefined && !props?.hideInfoBar ? (
864
+ <Box sx={{ display: "flex" }}>
865
+ {props?.hideBackButton === undefined && !props?.hideBackButton ? (
866
+ <IconButton
867
+ onClick={() => {
868
+ navigate(-1, { replace: true });
869
+ }}
870
+ >
871
+ <FontAwesomeIcon
872
+ icon={themeDirection === "ltr" ? "arrow-left" : "arrow-right"}
873
+ />
874
+ </IconButton>
875
+ ) : (
876
+ <></>
877
+ )}
878
+ <Box
879
+ sx={{
880
+ flex: 1,
881
+ display: "flex",
882
+ alignItems: "center",
883
+ justifyContent: "center",
884
+ }}
885
+ >
886
+ {props?.girdIcon ? (
887
+ <FontAwesomeIcon
888
+ icon={props.girdIcon}
889
+ style={{ marginRight: 5, marginLeft: 5 }}
890
+ />
891
+ ) : (
892
+ <></>
893
+ )}
894
+ <Typography variant="h5">
895
+ {props?.gridTitle
896
+ ? capitalizeFirstLetter(t(props?.gridTitle))
897
+ : ""}
898
+ </Typography>
899
+ </Box>
900
+ </Box>
901
+ ) : (
902
+ <></>
903
+ )}
904
+
905
+ {props?.gridLoadParameters &&
906
+ props?.gridLoadParameters.length > 0 &&
907
+ props?.gridLoadParametersValues &&
908
+ props?.setGridLoadParametersValues ? (
909
+ <Accordion defaultExpanded sx={{ width: "100%" }}>
910
+ <AccordionSummary expandIcon={<GridExpandMoreIcon />}>
911
+ <Box
912
+ sx={{
913
+ display: "flex",
914
+ alignItems: "center",
915
+ justifyContent: "center",
916
+ }}
917
+ >
918
+ <FontAwesomeIcon
919
+ style={{ marginLeft: 5, marginRight: 5 }}
920
+ icon="search"
921
+ />
922
+ <Typography component="span">Filters</Typography>
923
+ </Box>
924
+ </AccordionSummary>
925
+ <AccordionDetails>
926
+ <Box>
927
+ <TemplateForm
928
+ saveButtonSpecs={{
929
+ label: t("SEARCH_BTN_LABEL"),
930
+ icon: "search",
931
+ actionButtonVariant: "outlined",
932
+ actionButtonColor: "success",
933
+ }}
934
+ cancelButtonSpecs={{
935
+ label: t("RESET_BTN_LABEL"),
936
+ icon: "eraser",
937
+ actionButtonVariant: "outlined",
938
+ actionButtonColor: "error",
939
+ }}
940
+ apiActions={{
941
+ deleteRecordById: async () => {
942
+ return true;
943
+ },
944
+ saveRecord: async (params) => {
945
+ if (params != undefined) {
946
+ props.setGridLoadParametersValues(params);
947
+ } else {
948
+ props.setGridLoadParametersValues({});
949
+ }
950
+ props.apiActions.reloadData(params);
951
+ },
952
+ reloadData: async () => {},
953
+ loadRecordById: async () => {},
954
+ }}
955
+ elements={props.gridLoadParameters}
956
+ />
957
+ </Box>
958
+ </AccordionDetails>
959
+ </Accordion>
960
+ ) : (
961
+ <></>
962
+ )}
963
+ {props?.attachment ? (
964
+ <AttachmentWindow>
965
+ <AttachmentPanel
966
+ attachmentCode={props.attachment.attachmentCode}
967
+ refKey={selectedRecord[props?.keyColumnName || "id"]}
968
+ enableAttachment={attachmentPanelEnabledForRecord}
969
+ />
970
+ </AttachmentWindow>
971
+ ) : (
972
+ <></>
973
+ )}
974
+ {props?.workFlowDocumentCode ? (
975
+ <WorkFlowWindow>
976
+ <WorkflowDocumentPanel
977
+ workFlowDocumentCode={props.workFlowDocumentCode}
978
+ refDocumentId={selectedRecord[props?.keyColumnName || "id"]}
979
+ postActionCallBk={() => {
980
+ setWorkFlowWindowState(false);
981
+ props.apiActions.reloadData(props.gridLoadParametersValues);
982
+ }}
983
+ cancelActionCallBk={() => {
984
+ setWorkFlowWindowState(false);
985
+ props.apiActions.reloadData(props.gridLoadParametersValues);
986
+ }}
987
+ />
988
+ </WorkFlowWindow>
989
+ ) : (
990
+ <></>
991
+ )}
992
+ <DataGridPremium
993
+ {...props?.muiProps}
994
+ slots={{ toolbar: TemplateGridTopBar }}
995
+ slotProps={{
996
+ toolbar: {
997
+ templateProps: {
998
+ ...props,
999
+ rowSelectionModel: rowSelectionModel,
1000
+ data: props?.data,
1001
+ },
1002
+ handleCreateNewRecord,
1003
+ clearGridState,
1004
+ } as any,
1005
+ }}
1006
+ getRowId={(record: any) => {
1007
+ return record[keyColumnName];
1008
+ }}
1009
+ showToolbar={true}
1010
+ rows={props?.data}
1011
+ columns={adjustedColumns}
1012
+ checkboxSelection
1013
+ editMode="row"
1014
+ onRowEditStop={(params, event) => {
1015
+ if (params.reason === "rowFocusOut") {
1016
+ event.defaultMuiPrevented = true;
1017
+ }
1018
+ }}
1019
+ rowModesModel={
1020
+ props.editMode.editMode == "row" ? rowModesModel : undefined
1021
+ }
1022
+ onRowModesModelChange={
1023
+ props.editMode.editMode == "row"
1024
+ ? handleRowModesModelChange
1025
+ : undefined
1026
+ }
1027
+ rowGroupingColumnMode="multiple"
1028
+ processRowUpdate={processRowUpdate}
1029
+ rowSelectionModel={rowSelectionModel}
1030
+ onRowSelectionModelChange={handleRowSelection}
1031
+ columnVisibilityModel={columnVisibilityModel}
1032
+ onColumnVisibilityModelChange={handleColumnVisibilityChange}
1033
+ onColumnOrderChange={handleColumnOrderChange}
1034
+ // onColumnWidthChange={handleColumnWidthChange}
1035
+ rowGroupingModel={gridRowGroupingModel}
1036
+ onRowGroupingModelChange={(model: GridRowGroupingModel) => {
1037
+ handleRowGroupChange(model);
1038
+ }}
1039
+ // pinnedColumns={pinnedColumns}
1040
+ // onPinnedColumnsChange={handlePinnedColumnsChange}
1041
+ sx={{ width: "100%" }}
1042
+ />
1043
+ </>
1044
+ );
1045
+ };
1046
+
1047
+ export default TemplateGrid;