@asaleh37/ui-base 1.2.30 → 25.1.9

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 (179) hide show
  1. package/.github/workflows/publish-npm.yml +49 -0
  2. package/README.md +51 -51
  3. package/__ODockerfile +14 -0
  4. package/dist/index.d.ts +92 -10
  5. package/dist/index.js +135 -7
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +135 -7
  8. package/dist/index.mjs.map +1 -1
  9. package/eslint.config.js +29 -29
  10. package/index.html +20 -13
  11. package/package-lock.json/342/200/216 +9040 -0
  12. package/package.json +122 -119
  13. package/public/bg.jpg +0 -0
  14. package/public/ezzsteel.png +0 -0
  15. package/public/logo.png +0 -0
  16. package/public/manifest.json +21 -0
  17. package/public/no_user.png +0 -0
  18. package/rollup.config-1748377725725.cjs +50 -50
  19. package/rollup.config.js +45 -45
  20. package/src/components/App.tsx +155 -123
  21. package/src/components/BaseApp.tsx +75 -53
  22. package/src/components/ExampleTrial.tsx +24 -0
  23. package/src/components/administration/admin/ChangePasswordPanel.tsx +128 -0
  24. package/src/components/administration/admin/CustomPersonGrid.tsx +361 -0
  25. package/src/components/administration/admin/OrgMemberRoleForm.tsx +83 -83
  26. package/src/components/administration/admin/OrgProvidedPersonGrid.tsx +347 -0
  27. package/src/components/administration/admin/OrganizationApplicationModuleGrid.tsx +107 -107
  28. package/src/components/administration/admin/OrganizationGrid.tsx +118 -82
  29. package/src/components/administration/admin/OrganizationMemberGrid.tsx +190 -176
  30. package/src/components/administration/admin/OrganizationMemberRoleGrid.tsx +87 -87
  31. package/src/components/administration/admin/OrganizationRankGrid.tsx +133 -133
  32. package/src/components/administration/admin/OrganizationUnitGrid.tsx +143 -143
  33. package/src/components/administration/admin/OrganizationUnitTypeGrid.tsx +108 -108
  34. package/src/components/administration/admin/PersonGrid.tsx +27 -231
  35. package/src/components/administration/admin/RoleAuthoritiesForm.tsx +82 -82
  36. package/src/components/administration/admin/SystemApplicationAuthorityGrid.tsx +117 -126
  37. package/src/components/administration/admin/SystemApplicationGrid.tsx +83 -83
  38. package/src/components/administration/admin/SystemApplicationModuleGrid.tsx +96 -96
  39. package/src/components/administration/admin/SystemApplicationRoleAuthorityGrid.tsx +75 -67
  40. package/src/components/administration/admin/SystemApplicationRoleGrid.tsx +116 -116
  41. package/src/components/administration/dev/AttachmentConfigGrid.tsx +224 -213
  42. package/src/components/administration/dev/AttachmentGrid.tsx +172 -172
  43. package/src/components/administration/dev/BluePrintGrid.tsx +129 -129
  44. package/src/components/administration/dev/DashboardGrid.tsx +173 -173
  45. package/src/components/administration/dev/DashboardWidgetGrid.tsx +164 -164
  46. package/src/components/administration/dev/DataQueryGrid.tsx +216 -206
  47. package/src/components/administration/dev/DataQueryParameterGrid.tsx +191 -191
  48. package/src/components/administration/dev/DataQueryParametersForm.tsx +84 -84
  49. package/src/components/administration/dev/DatasourceConnectionGrid.tsx +151 -150
  50. package/src/components/administration/dev/EntityParameterGrid.tsx +307 -279
  51. package/src/components/administration/dev/LookupGrid.tsx +120 -120
  52. package/src/components/administration/dev/MailAttachmentGrid.tsx +155 -155
  53. package/src/components/administration/dev/MailBodyGrid.tsx +216 -216
  54. package/src/components/administration/dev/MailNotificationQueueGrid.tsx +245 -245
  55. package/src/components/administration/dev/MailRecipientGrid.tsx +170 -169
  56. package/src/components/administration/dev/MailSenderConfigGrid.tsx +480 -478
  57. package/src/components/administration/dev/MailTemplateGrid.tsx +385 -384
  58. package/src/components/administration/dev/NotificationGrid.tsx +435 -432
  59. package/src/components/administration/dev/NotificationQueueGrid.tsx +222 -222
  60. package/src/components/administration/dev/ReportGrid.tsx +503 -504
  61. package/src/components/administration/dev/ReportParameterGrid.tsx +186 -186
  62. package/src/components/administration/dev/ReportParametersForm.tsx +84 -84
  63. package/src/components/administration/dev/WidgetGrid.tsx +380 -431
  64. package/src/components/administration/dev/WorkflowDocumentActionGrid.tsx +264 -264
  65. package/src/components/administration/dev/WorkflowDocumentActionHistoryGrid.tsx +172 -172
  66. package/src/components/administration/dev/WorkflowDocumentActionMailGrid.tsx +161 -161
  67. package/src/components/administration/dev/WorkflowDocumentGrid.tsx +357 -377
  68. package/src/components/administration/dev/WorkflowDocumentMailLogGrid.tsx +218 -218
  69. package/src/components/administration/dev/WorkflowDocumentStatusGrid.tsx +243 -243
  70. package/src/components/common/AzureLogin.tsx +222 -0
  71. package/src/components/common/ChangeOrgForm.tsx +85 -81
  72. package/src/components/common/Home.tsx +43 -44
  73. package/src/components/common/LanguageSwitcher.tsx +25 -25
  74. package/src/components/common/LayoutHandlers.tsx +11 -11
  75. package/src/components/common/LoadingMask.tsx +24 -24
  76. package/src/components/common/Login.tsx +268 -214
  77. package/src/components/common/MobileLogin.tsx +229 -0
  78. package/src/components/common/MyNotificationsPanel.tsx +109 -104
  79. package/src/components/common/NoLicenseComponent.tsx +79 -0
  80. package/src/components/common/NotificationItem.tsx +138 -138
  81. package/src/components/index.ts +10 -9
  82. package/src/components/msalConfig.ts +11 -0
  83. package/src/components/templates/DataEntryTemplates/DataEntryTypes.ts +361 -324
  84. package/src/components/templates/DataEntryTemplates/DataEntryUtil.ts +297 -248
  85. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormAction.tsx +60 -60
  86. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormElementField.tsx +238 -231
  87. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormElementGroup.tsx +108 -106
  88. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormFields/CheckBox.tsx +66 -64
  89. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormFields/ComboBox.tsx +164 -93
  90. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormFields/Datefield.tsx +70 -65
  91. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormFields/DatetimeField.tsx +71 -64
  92. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormFields/FiltersPanel.tsx +237 -237
  93. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormFields/SystemLookupCombobox.tsx +56 -55
  94. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormFields/TemplateTextField.tsx +20 -17
  95. package/src/components/templates/DataEntryTemplates/TemplateDataForm/TemplateForm.tsx +431 -388
  96. package/src/components/templates/DataEntryTemplates/TemplateDataGrid/DataGridColumnsUtil.tsx +197 -189
  97. package/src/components/templates/DataEntryTemplates/TemplateDataGrid/TemplateGrid.tsx +1044 -998
  98. package/src/components/templates/DataEntryTemplates/TemplateDataGrid/TemplateGridMultiRecordAction.tsx +89 -89
  99. package/src/components/templates/DataEntryTemplates/TemplateDataGrid/TemplateGridRecordAction.tsx +95 -95
  100. package/src/components/templates/DataEntryTemplates/TemplateDataGrid/TemplateGridTopBar.tsx +234 -227
  101. package/src/components/templates/TransferList.tsx +256 -257
  102. package/src/components/templates/Window/ConfirmationWindow.tsx +55 -55
  103. package/src/components/templates/attachment/AttachmentCard.tsx +141 -141
  104. package/src/components/templates/attachment/AttachmentImageViewer.tsx +85 -45
  105. package/src/components/templates/attachment/AttachmentPanel.tsx +285 -271
  106. package/src/components/templates/index.ts +35 -33
  107. package/src/components/templates/report/ExcelReportViewer.tsx +71 -72
  108. package/src/components/templates/report/ReportViewer.tsx +272 -383
  109. package/src/components/templates/visuals/DashboardRouteView.tsx +9 -9
  110. package/src/components/templates/visuals/DashboardViewer.tsx +192 -148
  111. package/src/components/templates/visuals/WidgetViewer.tsx +208 -198
  112. package/src/components/templates/visuals/charts/TemplateBarChart.tsx +23 -23
  113. package/src/components/templates/visuals/charts/TemplateDataCard.tsx +35 -35
  114. package/src/components/templates/visuals/charts/TemplateGauge.tsx +21 -21
  115. package/src/components/templates/visuals/charts/TemplateLineChart.tsx +22 -22
  116. package/src/components/templates/visuals/charts/TemplateLineProgress.tsx +42 -42
  117. package/src/components/templates/visuals/charts/TemplatePieChart.tsx +24 -24
  118. package/src/components/templates/workflow/WorkflowDocumentPanel.tsx +611 -606
  119. package/src/components/templates/workflow/WorkflowDocumentTimeLine.tsx +145 -140
  120. package/src/components/templates/workflow/WorkflowRouteComponent.tsx +14 -14
  121. package/src/examples/ExampleGrid.tsx +134 -0
  122. package/src/hooks/UseConfirmationWindow.tsx +56 -54
  123. package/src/hooks/UseMobile.tsx +13 -13
  124. package/src/hooks/UseSession.tsx +59 -40
  125. package/src/hooks/UseWindow.tsx +111 -107
  126. package/src/hooks/index.ts +22 -7
  127. package/src/hooks/useApiActions.ts +124 -124
  128. package/src/hooks/useAxios.tsx +340 -316
  129. package/src/hooks/useCommonStore.tsx +29 -0
  130. package/src/hooks/useInterval.tsx +23 -23
  131. package/src/hooks/useLoadingMask.tsx +16 -16
  132. package/src/hooks/useLookupGridColumn.tsx +35 -35
  133. package/src/hooks/useParameterPanel.tsx +159 -0
  134. package/src/index.ts +4 -4
  135. package/src/layout/DrawerHeader.tsx +10 -10
  136. package/src/layout/Layout.tsx +102 -90
  137. package/src/layout/MainContent.tsx +115 -114
  138. package/src/layout/MobileDrawer.tsx +103 -103
  139. package/src/layout/NavigationTree.tsx +309 -291
  140. package/src/layout/NotificationButton.tsx +207 -207
  141. package/src/layout/RouteWrapper.tsx +63 -36
  142. package/src/layout/SideBar.tsx +85 -85
  143. package/src/layout/TopBar.tsx +317 -217
  144. package/src/locales/arabic/adminLocalsAr.json +94 -93
  145. package/src/locales/arabic/common.json +44 -44
  146. package/src/locales/arabic/devLocalsAr.json +317 -317
  147. package/src/locales/arabic/index.ts +9 -9
  148. package/src/locales/english/adminLocalsEn.json +97 -96
  149. package/src/locales/english/common.json +43 -43
  150. package/src/locales/english/devLocalsEn.json +318 -318
  151. package/src/locales/english/index.ts +9 -9
  152. package/src/locales/i18n.ts +8 -8
  153. package/src/locales/index.ts +9 -9
  154. package/src/main.tsx +41 -23
  155. package/src/navigationItems/Administration/adminNavigationItems.tsx +231 -222
  156. package/src/navigationItems/Administration/index.tsx +16 -16
  157. package/src/navigationItems/common/CommonNavigationItems.tsx +12 -12
  158. package/src/navigationItems/common/index.tsx +7 -7
  159. package/src/navigationItems/index.tsx +35 -34
  160. package/src/redux/features/administration/AdministrationStoresMetaData.ts +164 -126
  161. package/src/redux/features/common/AppInfoSlice.ts +93 -63
  162. package/src/redux/features/common/AppLayoutSlice.ts +29 -29
  163. package/src/redux/features/common/CommonStoreSlice.ts +44 -44
  164. package/src/redux/features/common/LoadingMaskSlice.ts +30 -30
  165. package/src/redux/features/common/SideBarSlice.ts +27 -27
  166. package/src/redux/features/common/UserSessionSlice.ts +54 -54
  167. package/src/redux/store.ts +29 -29
  168. package/src/routes/administration/adminRoutes.tsx +99 -99
  169. package/src/routes/administration/devRoutes.tsx +129 -129
  170. package/src/routes/administration/index.ts +8 -8
  171. package/src/routes/index.ts +5 -11
  172. package/src/routes/types/index.ts +6 -5
  173. package/src/styles/index.css +19 -19
  174. package/src/types/index.ts +8 -7
  175. package/src/util/AppUtils.ts +73 -53
  176. package/src/util/constants.ts +6 -6
  177. package/src/util/index.ts +5 -2
  178. package/tsconfig.json +135 -135
  179. package/vite.config.ts +24 -23
@@ -1,388 +1,431 @@
1
- import { Box } from "@mui/system";
2
- import React, { useEffect, useState } from "react";
3
- import * as z from "zod";
4
- import { useForm } from "react-hook-form";
5
- import { zodResolver } from "@hookform/resolvers/zod";
6
- import { useParams } from "react-router-dom";
7
- import { Button, Grid2, Icon, IconButton, Tooltip } from "@mui/material";
8
- import { toast } from "react-toastify";
9
- import FormElementGroup from "./FormElementGroup";
10
- import FormElementField from "./FormElementField";
11
- import FormAction from "./FormAction";
12
- import { constructValidationSchema, getAllFields } from "../DataEntryUtil";
13
- import {
14
- FormElementProps,
15
- RecordAction,
16
- TemplateFormProps,
17
- } from "../DataEntryTypes";
18
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
19
- import { useTranslation } from "react-i18next";
20
- import { useWindow } from "../../../../hooks";
21
- import AttachmentPanel from "../../attachment/AttachmentPanel";
22
- import WorkflowDocumentPanel from "../../workflow/WorkflowDocumentPanel";
23
-
24
- const TemplateForm: React.FC<TemplateFormProps> = (
25
- props: TemplateFormProps
26
- ) => {
27
- const { Window: AttachmentWindow, setWindowState: setAttachmentWindowState } =
28
- useWindow({
29
- windowTitle: "Attachments",
30
- windowIcon: "paperclip",
31
- width: "fit-content",
32
- height: "fit-content",
33
- minHeight: 500,
34
- minWidth: 400,
35
- });
36
- const { Window: WorkflowWindow, setWindowState: setWorkflowWindowState } =
37
- useWindow({
38
- windowTitle: "Approvals",
39
- windowIcon: "stamp",
40
- width: "fit-content",
41
- height: "fit-content",
42
- minHeight: 500,
43
- minWidth: 400,
44
- });
45
- const [attachmentPanelEnabledForRecord, setAttachmentPanelEnabledForRecord] =
46
- useState<boolean>(true);
47
- const { t } = useTranslation();
48
- const fields = getAllFields(props.elements);
49
- const initiallyHiddenFields = [];
50
- for (const field of fields) {
51
- if (field?.hidden) {
52
- initiallyHiddenFields.push(field.fieldName);
53
- }
54
- }
55
- const [hiddenFields, setHiddenFields] = useState<string[]>(
56
- initiallyHiddenFields
57
- );
58
- const initialValues: any = {};
59
- for (const element of props.elements) {
60
- if (
61
- element?.type === "field" &&
62
- element?.mode === "props" &&
63
- element?.props?.defaultValue
64
- ) {
65
- initialValues[element.props.fieldName] = element.props.defaultValue;
66
- }
67
- }
68
- const [disabledFields, setDisabledFields] = useState<string[]>([]);
69
- const formSchema = z.object(constructValidationSchema(fields));
70
- type FormData = z.infer<typeof formSchema>;
71
- const formManager = useForm<FormData>({
72
- resolver: zodResolver(formSchema),
73
- defaultValues: initialValues,
74
- });
75
- const formValues = formManager.watch();
76
- const pathParameters = useParams();
77
- const formRouteRecordIdParamName: any = props?.formRouteRecordIdParamName;
78
-
79
- const loadRecord = async () => {
80
- let idToLoad = null;
81
- if (props?.recordIdToEdit) {
82
- idToLoad = props.recordIdToEdit;
83
- } else if (
84
- formRouteRecordIdParamName &&
85
- pathParameters[formRouteRecordIdParamName]
86
- ) {
87
- idToLoad = pathParameters[formRouteRecordIdParamName];
88
- }
89
- if (idToLoad) {
90
- const retrievedRecord: any = await props.apiActions.loadRecordById(
91
- idToLoad
92
- );
93
- if (retrievedRecord) {
94
- formManager.reset({ ...retrievedRecord });
95
- if (props?.formLoadCallBk) {
96
- props?.formLoadCallBk(formActions, formManager, retrievedRecord);
97
- }
98
- for (const field of fields) {
99
- if (
100
- field?.fieldType === "combobox" &&
101
- retrievedRecord[field.fieldName]
102
- ) {
103
- formManager.setValue(
104
- field.fieldName,
105
- retrievedRecord[field.fieldName] + ""
106
- );
107
- }
108
- }
109
- }
110
- } else {
111
- formManager.reset({});
112
- }
113
- };
114
-
115
- const saveRecord = async (record: any) => {
116
- if (props?.preSaveValidation && !props.preSaveValidation(record)) {
117
- return;
118
- }
119
- if (record) {
120
- const savedRecord: any = await props.apiActions.saveRecord(record);
121
- if (savedRecord) {
122
- formManager.reset({ ...savedRecord });
123
- if (props?.formSavedSuccessfullyCallBk) {
124
- props.formSavedSuccessfullyCallBk(savedRecord);
125
- }
126
- if (props?.formCloseCallBk) {
127
- props.formCloseCallBk();
128
- }
129
- }
130
- }
131
- };
132
-
133
- const formActions = {
134
- setFieldValue: (fieldName: string, fieldValue: any) => {
135
- formManager.setValue(fieldName, fieldValue);
136
- },
137
- hideField: (fieldName: string) => {
138
- setHiddenFields((oldValues) => {
139
- const newValues = [...oldValues, fieldName];
140
- return newValues;
141
- });
142
- },
143
- showField: (fieldName: string) => {
144
- setHiddenFields((oldValues) => {
145
- const newValues = oldValues.filter((x) => x !== fieldName);
146
- return newValues;
147
- });
148
- },
149
- disableField: (fieldName: string) => {
150
- setDisabledFields((oldValues) => {
151
- const newValues = [...oldValues, fieldName];
152
- return newValues;
153
- });
154
- },
155
- enableField: (fieldName: string) => {
156
- setDisabledFields((oldValues) => {
157
- const newValues = oldValues.filter((x) => x !== fieldName);
158
- return newValues;
159
- });
160
- },
161
- };
162
-
163
- useEffect(() => {
164
- loadRecord();
165
- }, [props?.recordIdToEdit]);
166
-
167
- useEffect(() => {
168
- if (props?.attachment && props?.attachment?.enableAttachFn) {
169
- setAttachmentPanelEnabledForRecord(
170
- props.attachment.enableAttachFn(formValues)
171
- );
172
- } else {
173
- setAttachmentPanelEnabledForRecord(true);
174
- }
175
- }, [formValues]);
176
-
177
- return (
178
- <>
179
- {props?.attachment ? (
180
- <AttachmentWindow>
181
- <AttachmentPanel
182
- attachmentCode={props.attachment.attachmentCode}
183
- refKey={formValues[props?.keyColumnName || "id"]}
184
- enableAttachment={attachmentPanelEnabledForRecord}
185
- />
186
- </AttachmentWindow>
187
- ) : (
188
- <></>
189
- )}
190
- {props?.workFlowDocumentCode ? (
191
- <WorkflowWindow>
192
- <WorkflowDocumentPanel
193
- workFlowDocumentCode={props.workFlowDocumentCode}
194
- refDocumentId={formValues[props?.keyColumnName || "id"]}
195
- postActionCallBk={() => {
196
- setWorkflowWindowState(false);
197
- loadRecord();
198
- }}
199
- cancelActionCallBk={() => {
200
- setWorkflowWindowState(false);
201
- }}
202
- />
203
- </WorkflowWindow>
204
- ) : (
205
- <></>
206
- )}
207
-
208
- <Box
209
- sx={{
210
- display: "flex",
211
- flex: 1,
212
- width: "100%",
213
- height: "fit-content",
214
- flexDirection: "column",
215
- alignItems: "center",
216
- overflow: "auto",
217
- }}
218
- >
219
- <Grid2 sx={{ width: "100%" }} container>
220
- {props.elements.map((formElement: FormElementProps, index) => {
221
- if (formElement.type === "group") {
222
- return (
223
- <FormElementGroup
224
- key={index}
225
- {...formElement.props}
226
- formManager={formManager}
227
- formValues={formValues}
228
- formActions={formActions}
229
- hiddenFields={hiddenFields}
230
- disabledFields={disabledFields}
231
- />
232
- );
233
- } else if (
234
- formElement.type === "field" &&
235
- formElement.mode === "props"
236
- ) {
237
- return (
238
- <FormElementField
239
- key={index}
240
- fieldInfo={formElement.props}
241
- formManager={formManager}
242
- formValues={formValues}
243
- formActions={formActions}
244
- hiddenFields={hiddenFields}
245
- disabledFields={disabledFields}
246
- />
247
- );
248
- } else if (
249
- formElement.type === "field" &&
250
- formElement.mode === "node"
251
- ) {
252
- return (
253
- <Grid2
254
- key={index}
255
- size={
256
- formElement?.props?.formProps?.fieldSize || {
257
- lg: 3,
258
- md: 6,
259
- xs: 12,
260
- }
261
- }
262
- sx={{ padding: 1, width: "100%" }}
263
- >
264
- <formElement.node
265
- formManager={formManager}
266
- formValues={formValues}
267
- />
268
- </Grid2>
269
- );
270
- }
271
- })}
272
- </Grid2>
273
- </Box>
274
-
275
- <Box
276
- sx={{
277
- display: "flex",
278
- width: "100%",
279
- alignItems: "center",
280
- justifyContent: "flex-start",
281
- }}
282
- >
283
- {formValues[props?.keyColumnName || "id"] ? (
284
- props?.attachment ? (
285
- <Tooltip title="Attachments">
286
- <IconButton
287
- onClick={() => {
288
- setAttachmentWindowState(true);
289
- }}
290
- >
291
- <FontAwesomeIcon icon="paperclip" />
292
- </IconButton>
293
- </Tooltip>
294
- ) : null
295
- ) : null}
296
- {formValues[props?.keyColumnName || "id"] ? (
297
- props?.workFlowDocumentCode ? (
298
- <Tooltip title="Approvals">
299
- <IconButton
300
- onClick={() => {
301
- setWorkflowWindowState(true);
302
- }}
303
- >
304
- <FontAwesomeIcon icon="stamp" />
305
- </IconButton>
306
- </Tooltip>
307
- ) : null
308
- ) : null}
309
- {props?.actions ? (
310
- formValues[props?.keyColumnName || "id"] ? (
311
- props.actions.map((action: RecordAction) => {
312
- if (action?.formActionProps?.enabled === true) {
313
- return <FormAction {...action} record={formValues} />;
314
- } else {
315
- return <></>;
316
- }
317
- })
318
- ) : (
319
- <></>
320
- )
321
- ) : (
322
- <></>
323
- )}
324
- <div style={{ flex: 1 }}></div>
325
- <Button
326
- variant={
327
- props?.saveButtonSpecs?.actionButtonVariant
328
- ? props.saveButtonSpecs.actionButtonVariant
329
- : "contained"
330
- }
331
- sx={{ m: 1 }}
332
- startIcon={
333
- props?.saveButtonSpecs?.icon ? (
334
- <FontAwesomeIcon icon={props.saveButtonSpecs.icon} />
335
- ) : null
336
- }
337
- color={
338
- props?.saveButtonSpecs?.actionButtonColor
339
- ? props.saveButtonSpecs.actionButtonColor
340
- : "primary"
341
- }
342
- onClick={formManager.handleSubmit(
343
- (values) => {
344
- console.log("form values", values);
345
- saveRecord(values);
346
- },
347
- (errors) => {
348
- toast.error(
349
- "Form Data is not valid, make sure you have all field with valid data"
350
- );
351
- console.log("form validation error", errors);
352
- }
353
- )}
354
- >
355
- {t(props?.saveButtonSpecs?.label || "SAVE_BTN_LABEL")}
356
- </Button>
357
- <Button
358
- variant={
359
- props?.cancelButtonSpecs?.actionButtonVariant
360
- ? props.cancelButtonSpecs.actionButtonVariant
361
- : "contained"
362
- }
363
- startIcon={
364
- props?.cancelButtonSpecs?.icon ? (
365
- <FontAwesomeIcon icon={props.cancelButtonSpecs.icon} />
366
- ) : null
367
- }
368
- color={
369
- props?.cancelButtonSpecs?.actionButtonColor
370
- ? props.cancelButtonSpecs.actionButtonColor
371
- : "error"
372
- }
373
- sx={{ m: 1 }}
374
- onClick={() => {
375
- if (props?.formCloseCallBk) {
376
- props.formCloseCallBk();
377
- }
378
- formManager.reset(initialValues);
379
- }}
380
- >
381
- {t(props?.cancelButtonSpecs?.label || "CANCEL_BTN_LABEL")}
382
- </Button>
383
- </Box>
384
- </>
385
- );
386
- };
387
-
388
- export default TemplateForm;
1
+ import { Box } from "@mui/system";
2
+ import React, { useEffect, useState } from "react";
3
+ import * as z from "zod";
4
+ import { useForm } from "react-hook-form";
5
+ import { zodResolver } from "@hookform/resolvers/zod";
6
+ import { useParams } from "react-router-dom";
7
+ import { Button, Grid2, Icon, IconButton, Tooltip } from "@mui/material";
8
+ import { toast } from "react-toastify";
9
+ import FormElementGroup from "./FormElementGroup";
10
+ import FormElementField from "./FormElementField";
11
+ import FormAction from "./FormAction";
12
+ import { constructValidationSchema, getAllFields } from "../DataEntryUtil";
13
+ import {
14
+ FormElementProps,
15
+ RecordAction,
16
+ TemplateFormProps,
17
+ } from "../DataEntryTypes";
18
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
19
+ import { useTranslation } from "react-i18next";
20
+ import { useWindow } from "../../../../hooks";
21
+ import AttachmentPanel from "../../attachment/AttachmentPanel";
22
+ import WorkflowDocumentPanel from "../../workflow/WorkflowDocumentPanel";
23
+
24
+ const TemplateForm: React.FC<TemplateFormProps> = (
25
+ props: TemplateFormProps
26
+ ) => {
27
+ const { Window: AttachmentWindow, setWindowState: setAttachmentWindowState } =
28
+ useWindow({
29
+ windowTitle: "Attachments",
30
+ windowIcon: "paperclip",
31
+ width: "fit-content",
32
+ height: "fit-content",
33
+ minHeight: 500,
34
+ minWidth: 400,
35
+ });
36
+
37
+ const [attachmentPanelEnabledForRecord, setAttachmentPanelEnabledForRecord] =
38
+ useState<boolean>(true);
39
+ const { t } = useTranslation();
40
+ const fields = getAllFields(props.elements);
41
+
42
+ const [disabledFields, setDisabledFields] = useState<string[]>([]);
43
+ const [hiddenFields, setHiddenFields] = useState<string[]>([]);
44
+ const initialValues: any = {};
45
+ for (const element of props.elements) {
46
+ if (
47
+ element?.type === "field" &&
48
+ element?.mode === "props" &&
49
+ element?.props?.defaultValue
50
+ ) {
51
+ initialValues[element.props.fieldName] = element.props.defaultValue;
52
+ }
53
+ }
54
+
55
+ let formSchema = null;
56
+ if (props?.validationSchema) {
57
+ formSchema = props.validationSchema;
58
+ } else {
59
+ formSchema = z.object(constructValidationSchema(fields));
60
+ }
61
+ type FormData = z.infer<typeof formSchema>;
62
+ const formManager = useForm<FormData>({
63
+ resolver: zodResolver(formSchema),
64
+ defaultValues: initialValues,
65
+ });
66
+ const formValues = formManager.watch();
67
+ const pathParameters = useParams();
68
+ const formRouteRecordIdParamName: any = props?.formRouteRecordIdParamName;
69
+
70
+ const loadRecord = async () => {
71
+ let idToLoad = null;
72
+ if (props?.recordIdToEdit) {
73
+ idToLoad = props.recordIdToEdit;
74
+ } else if (
75
+ formRouteRecordIdParamName &&
76
+ pathParameters[formRouteRecordIdParamName]
77
+ ) {
78
+ idToLoad = pathParameters[formRouteRecordIdParamName];
79
+ }
80
+ if (idToLoad) {
81
+ const retrievedRecord: any = await props.apiActions.loadRecordById(
82
+ idToLoad
83
+ );
84
+ if (retrievedRecord) {
85
+ formManager.reset({ ...retrievedRecord });
86
+ for (const field of fields) {
87
+ if (
88
+ field?.fieldType === "combobox" &&
89
+ retrievedRecord[field.fieldName]
90
+ ) {
91
+ if (
92
+ field?.comboboxValueDataType &&
93
+ field?.comboboxValueDataType === "string"
94
+ ) {
95
+ formManager.setValue(
96
+ field.fieldName,
97
+ retrievedRecord[field.fieldName] + ""
98
+ );
99
+ } else {
100
+ formManager.setValue(
101
+ field.fieldName,
102
+ retrievedRecord[field.fieldName]
103
+ );
104
+ }
105
+ }
106
+ }
107
+ if (props?.formValuesChangeCallBk) {
108
+ props?.formValuesChangeCallBk(
109
+ retrievedRecord,
110
+ formActions,
111
+ formManager
112
+ );
113
+ }
114
+ }
115
+ } else {
116
+ formManager.reset({});
117
+ props.formValuesChangeCallBk(formValues, formActions, formManager);
118
+ }
119
+ };
120
+
121
+ const saveRecord = async (record: any) => {
122
+ if (props?.preSaveValidation && !props.preSaveValidation(record)) {
123
+ return;
124
+ }
125
+ if (record) {
126
+ const savedRecord: any = await props.apiActions.saveRecord(record);
127
+ if (savedRecord) {
128
+ formManager.reset({ ...savedRecord });
129
+ if (props?.formSavedSuccessfullyCallBk) {
130
+ props.formSavedSuccessfullyCallBk(savedRecord);
131
+ }
132
+ if (props?.formCloseCallBk) {
133
+ props.formCloseCallBk();
134
+ }
135
+ }
136
+ }
137
+ };
138
+
139
+ const { Window: WorkflowWindow, setWindowState: setWorkflowWindowState } =
140
+ useWindow({
141
+ windowTitle: "Approvals",
142
+ windowIcon: "stamp",
143
+ height: "fit-content",
144
+ minHeight: 500,
145
+ width: "fit-content",
146
+ // width: 1100,
147
+ onCloseCallBack: async () => {
148
+ await loadRecord();
149
+ },
150
+ });
151
+
152
+ const formActions = {
153
+ setFieldValue: (fieldName: string, fieldValue: any) => {
154
+ formManager.setValue(fieldName, fieldValue);
155
+ },
156
+ hideField: (fieldName: string) => {
157
+ setHiddenFields((oldValues) => {
158
+ const newValues = [...oldValues, fieldName];
159
+ return newValues;
160
+ });
161
+ },
162
+ showField: (fieldName: string) => {
163
+ setHiddenFields((oldValues) => {
164
+ const newValues = oldValues.filter((x) => x !== fieldName);
165
+ return newValues;
166
+ });
167
+ },
168
+ disableField: (fieldName: string) => {
169
+ setDisabledFields((oldValues) => {
170
+ const newValues = [...oldValues, fieldName];
171
+ return newValues;
172
+ });
173
+ },
174
+ enableField: (fieldName: string) => {
175
+ setDisabledFields((oldValues) => {
176
+ const newValues = oldValues.filter((x) => x !== fieldName);
177
+ return newValues;
178
+ });
179
+ },
180
+ };
181
+
182
+ useEffect(() => {
183
+ const initiallyHiddenFields = [];
184
+ const initiallyDisabledFields = [];
185
+ for (const field of fields) {
186
+ if (field?.hidden) {
187
+ initiallyHiddenFields.push(field.fieldName);
188
+ }
189
+ if (field?.disabled) {
190
+ initiallyDisabledFields.push(field.fieldName);
191
+ }
192
+ }
193
+ setHiddenFields(initiallyHiddenFields);
194
+ setDisabledFields(initiallyDisabledFields);
195
+ }, [props.elements]);
196
+ useEffect(() => {
197
+ loadRecord();
198
+ }, [props?.recordIdToEdit]);
199
+
200
+ useEffect(() => {
201
+ if (props?.attachment && props?.attachment?.enableAttachFn) {
202
+ setAttachmentPanelEnabledForRecord(
203
+ props.attachment.enableAttachFn(formValues)
204
+ );
205
+ } else {
206
+ setAttachmentPanelEnabledForRecord(true);
207
+ }
208
+ }, [formValues]);
209
+ return (
210
+ <>
211
+ {props?.attachment ? (
212
+ <AttachmentWindow>
213
+ <AttachmentPanel
214
+ attachmentCode={props.attachment.attachmentCode}
215
+ refKey={formValues[props?.keyColumnName || "id"]}
216
+ enableAttachment={attachmentPanelEnabledForRecord}
217
+ />
218
+ </AttachmentWindow>
219
+ ) : (
220
+ <></>
221
+ )}
222
+ {props?.workFlowDocumentCode ? (
223
+ <WorkflowWindow>
224
+ <WorkflowDocumentPanel
225
+ workFlowDocumentCode={props.workFlowDocumentCode}
226
+ refDocumentId={formValues[props?.keyColumnName || "id"]}
227
+ postActionCallBk={() => {
228
+ setWorkflowWindowState(false);
229
+ loadRecord();
230
+ }}
231
+ cancelActionCallBk={() => {
232
+ setWorkflowWindowState(false);
233
+ loadRecord();
234
+ }}
235
+ />
236
+ </WorkflowWindow>
237
+ ) : (
238
+ <></>
239
+ )}
240
+
241
+ <Box
242
+ sx={{
243
+ display: "flex",
244
+ flex: 1,
245
+ width: "100%",
246
+ height: "fit-content",
247
+ flexDirection: "column",
248
+ alignItems: "center",
249
+ overflow: "auto",
250
+ }}
251
+ >
252
+ <Grid2 sx={{ width: "100%" }} container>
253
+ {props.elements.map((formElement: FormElementProps, index) => {
254
+ if (formElement.type === "group") {
255
+ return (
256
+ <FormElementGroup
257
+ key={index}
258
+ {...formElement.props}
259
+ formManager={formManager}
260
+ formValues={formValues}
261
+ formActions={formActions}
262
+ hiddenFields={hiddenFields}
263
+ disabledFields={disabledFields}
264
+ formValuesChangeCallBk={props.formValuesChangeCallBk}
265
+ />
266
+ );
267
+ } else if (
268
+ formElement.type === "field" &&
269
+ formElement.mode === "props"
270
+ ) {
271
+ return (
272
+ <FormElementField
273
+ key={index}
274
+ fieldInfo={formElement.props}
275
+ formManager={formManager}
276
+ formValues={formValues}
277
+ formActions={formActions}
278
+ hiddenFields={hiddenFields}
279
+ disabledFields={disabledFields}
280
+ formValuesChangeCallBk={props.formValuesChangeCallBk}
281
+ />
282
+ );
283
+ } else if (
284
+ formElement.type === "field" &&
285
+ formElement.mode === "node"
286
+ ) {
287
+ return (
288
+ <Grid2
289
+ key={index}
290
+ size={
291
+ formElement?.props?.formProps?.fieldSize || {
292
+ lg: 3,
293
+ md: 6,
294
+ xs: 12,
295
+ }
296
+ }
297
+ sx={{ padding: 1, width: "100%" }}
298
+ >
299
+ <formElement.node
300
+ formManager={formManager}
301
+ formValues={formValues}
302
+ />
303
+ </Grid2>
304
+ );
305
+ }
306
+ })}
307
+ </Grid2>
308
+ </Box>
309
+
310
+ <Box
311
+ sx={{
312
+ display: "flex",
313
+ width: "100%",
314
+ alignItems: "center",
315
+ justifyContent: "flex-start",
316
+ }}
317
+ >
318
+ {formValues[props?.keyColumnName || "id"] ? (
319
+ props?.attachment ? (
320
+ <Tooltip title="Attachments">
321
+ <IconButton
322
+ onClick={() => {
323
+ setAttachmentWindowState(true);
324
+ }}
325
+ >
326
+ <FontAwesomeIcon icon="paperclip" />
327
+ </IconButton>
328
+ </Tooltip>
329
+ ) : null
330
+ ) : null}
331
+ {formValues[props?.keyColumnName || "id"] ? (
332
+ props?.workFlowDocumentCode ? (
333
+ <Tooltip title="Approvals">
334
+ <IconButton
335
+ onClick={() => {
336
+ setWorkflowWindowState(true);
337
+ }}
338
+ >
339
+ <FontAwesomeIcon icon="stamp" />
340
+ </IconButton>
341
+ </Tooltip>
342
+ ) : null
343
+ ) : null}
344
+ {props?.actions ? (
345
+ props.actions.map((action: RecordAction) => {
346
+ if (action?.isIdRequired === false) {
347
+ return <FormAction {...action} record={formValues} />;
348
+ } else if (formValues[props?.keyColumnName || "id"]) {
349
+ if (action?.formActionProps?.enabled === true) {
350
+ return <FormAction {...action} record={formValues} />;
351
+ } else {
352
+ return <></>;
353
+ }
354
+ }
355
+ })
356
+ ) : (
357
+ <></>
358
+ )}
359
+ <div style={{ flex: 1 }}></div>
360
+ {props?.saveButtonSpecs?.hidden ? (
361
+ <></>
362
+ ) : (
363
+ <Button
364
+ variant={
365
+ props?.saveButtonSpecs?.actionButtonVariant
366
+ ? props.saveButtonSpecs.actionButtonVariant
367
+ : "contained"
368
+ }
369
+ sx={{ m: 1 }}
370
+ startIcon={
371
+ props?.saveButtonSpecs?.icon ? (
372
+ <FontAwesomeIcon icon={props.saveButtonSpecs.icon} />
373
+ ) : null
374
+ }
375
+ color={
376
+ props?.saveButtonSpecs?.actionButtonColor
377
+ ? props.saveButtonSpecs.actionButtonColor
378
+ : "primary"
379
+ }
380
+ onClick={formManager.handleSubmit(
381
+ (values) => {
382
+ saveRecord(values);
383
+ },
384
+ (errors) => {
385
+ toast.error(
386
+ "Form Data is not valid, make sure you have all field with valid data"
387
+ );
388
+ console.log("form validation error", errors);
389
+ }
390
+ )}
391
+ >
392
+ {t(props?.saveButtonSpecs?.label || "SAVE_BTN_LABEL")}
393
+ </Button>
394
+ )}
395
+
396
+ {props?.cancelButtonSpecs?.hidden ? (
397
+ <></>
398
+ ) : (
399
+ <Button
400
+ variant={
401
+ props?.cancelButtonSpecs?.actionButtonVariant
402
+ ? props.cancelButtonSpecs.actionButtonVariant
403
+ : "contained"
404
+ }
405
+ startIcon={
406
+ props?.cancelButtonSpecs?.icon ? (
407
+ <FontAwesomeIcon icon={props.cancelButtonSpecs.icon} />
408
+ ) : null
409
+ }
410
+ color={
411
+ props?.cancelButtonSpecs?.actionButtonColor
412
+ ? props.cancelButtonSpecs.actionButtonColor
413
+ : "error"
414
+ }
415
+ sx={{ m: 1 }}
416
+ onClick={() => {
417
+ if (props?.formCloseCallBk) {
418
+ props.formCloseCallBk();
419
+ }
420
+ formManager.reset(initialValues);
421
+ }}
422
+ >
423
+ {t(props?.cancelButtonSpecs?.label || "CANCEL_BTN_LABEL")}
424
+ </Button>
425
+ )}
426
+ </Box>
427
+ </>
428
+ );
429
+ };
430
+
431
+ export default TemplateForm;