@asaleh37/ui-base 1.2.17 → 1.2.19

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 (58) hide show
  1. package/.env.development +1 -1
  2. package/dist/index.d.ts +27 -5
  3. package/dist/index.js +5 -5
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +5 -5
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +1 -1
  8. package/public/no_image.png +0 -0
  9. package/src/components/administration/admin/OrganizationApplicationModuleGrid.tsx +10 -34
  10. package/src/components/administration/admin/OrganizationGrid.tsx +4 -87
  11. package/src/components/administration/admin/OrganizationRankGrid.tsx +34 -16
  12. package/src/components/administration/admin/OrganizationUnitGrid.tsx +34 -30
  13. package/src/components/administration/admin/OrganizationUnitTypeGrid.tsx +3 -16
  14. package/src/components/administration/admin/PersonGrid.tsx +38 -1
  15. package/src/components/administration/admin/RoleAuthoritiesForm.tsx +2 -4
  16. package/src/components/administration/admin/SystemApplicationAuthorityGrid.tsx +2 -18
  17. package/src/components/administration/admin/SystemApplicationGrid.tsx +5 -83
  18. package/src/components/administration/admin/SystemApplicationModuleGrid.tsx +2 -15
  19. package/src/components/administration/admin/SystemApplicationRoleGrid.tsx +1 -15
  20. package/src/components/administration/dev/AttachmentConfigGrid.tsx +213 -0
  21. package/src/components/administration/dev/AttachmentGrid.tsx +172 -0
  22. package/src/components/administration/dev/LookupGrid.tsx +1 -12
  23. package/src/components/common/Home.tsx +24 -22
  24. package/src/components/templates/DataEntryTemplates/DataEntryTypes.ts +13 -1
  25. package/src/components/templates/DataEntryTemplates/DataEntryUtil.ts +6 -0
  26. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormElementField.tsx +1 -3
  27. package/src/components/templates/DataEntryTemplates/TemplateDataForm/FormFields/SystemLookupCombobox.tsx +1 -1
  28. package/src/components/templates/DataEntryTemplates/TemplateDataForm/TemplateForm.tsx +100 -8
  29. package/src/components/templates/DataEntryTemplates/TemplateDataGrid/TemplateGrid.tsx +113 -4
  30. package/src/components/templates/attachment/AttachmentCard.tsx +126 -0
  31. package/src/components/templates/attachment/AttachmentImageViewer.tsx +44 -0
  32. package/src/components/templates/attachment/AttachmentPanel.tsx +269 -0
  33. package/src/components/templates/workflow/WorkflowDocumentPanel.tsx +4 -4
  34. package/src/hooks/UseSession.tsx +20 -2
  35. package/src/hooks/useAxios.tsx +71 -11
  36. package/src/hooks/useLookupGridColumn.tsx +2 -2
  37. package/src/layout/MainContent.tsx +49 -46
  38. package/src/layout/TopBar.tsx +6 -1
  39. package/src/locales/arabic/devLocalsAr.json +13 -1
  40. package/src/locales/english/devLocalsEn.json +13 -1
  41. package/src/main.tsx +3 -3
  42. package/src/navigationItems/Administration/adminNavigationItems.tsx +77 -3
  43. package/src/redux/features/administration/AdministrationStoresMetaData.ts +14 -6
  44. package/src/routes/administration/adminRoutes.tsx +21 -0
  45. package/src/routes/administration/devRoutes.tsx +24 -0
  46. package/public/icons/LICENSE.md +0 -5
  47. package/public/icons/arrow-clockwise.svg +0 -4
  48. package/public/icons/arrow-counterclockwise.svg +0 -4
  49. package/public/icons/journal-text.svg +0 -5
  50. package/public/icons/justify.svg +0 -3
  51. package/public/icons/text-center.svg +0 -3
  52. package/public/icons/text-left.svg +0 -3
  53. package/public/icons/text-paragraph.svg +0 -3
  54. package/public/icons/text-right.svg +0 -3
  55. package/public/icons/type-bold.svg +0 -3
  56. package/public/icons/type-italic.svg +0 -3
  57. package/public/icons/type-strikethrough.svg +0 -3
  58. package/public/icons/type-underline.svg +0 -3
@@ -1,3 +1,4 @@
1
+ import { attachment } from "./../attachment/AttachmentCard";
1
2
  import { IconProp } from "@fortawesome/fontawesome-svg-core";
2
3
  import {
3
4
  DataGridPremiumProps,
@@ -96,7 +97,14 @@ export type TemplateGridColDef = GridColDef & {
96
97
  valueField?: string;
97
98
  };
98
99
 
100
+ export type TemplateGridAttachmentProps = {
101
+ attachmentCode: string;
102
+ enableAttachFn?: (record: any) => boolean;
103
+ };
104
+
99
105
  export type TemplateGridProps = {
106
+ workFlowDocumentCode?: string;
107
+ attachment?: TemplateGridAttachmentProps;
100
108
  formElements: Array<FormElementProps>;
101
109
  gridStateKey?: string;
102
110
  data: Array<any>;
@@ -178,7 +186,8 @@ export type RecordFieldProps = {
178
186
  | "combobox"
179
187
  | "checkbox"
180
188
  | "html"
181
- | "lookup";
189
+ | "lookup"
190
+ | "custom";
182
191
  lookupType?: string;
183
192
  required?: boolean;
184
193
  disabled?: boolean;
@@ -259,6 +268,9 @@ export type FormActionProps = {
259
268
  };
260
269
 
261
270
  export type TemplateFormProps = {
271
+ workFlowDocumentCode?: string;
272
+ attachment?: TemplateGridAttachmentProps;
273
+ keyColumnName?: any;
262
274
  elements: Array<FormElementProps>;
263
275
  findByIdParamName?: string;
264
276
  recordIdToEdit?: any;
@@ -164,6 +164,12 @@ export const constructGridColumnsFromFields: (
164
164
  minWidth: 200,
165
165
  });
166
166
  columns.push(column);
167
+ } else {
168
+ const column: TemplateGridColDef = {
169
+ field: tableField.fieldName,
170
+ ...tableField?.gridProps?.muiProps,
171
+ };
172
+ columns.push(column);
167
173
  }
168
174
  }
169
175
  return columns;
@@ -218,9 +218,7 @@ const FormElementField: React.FC<FormElementFieldProps> = (
218
218
  fieldName
219
219
  ]?.message?.toString()}
220
220
  />
221
- ) : (
222
- <>Unknown field type</>
223
- )}
221
+ ) : null}
224
222
  </Grid2>
225
223
  ) : (
226
224
  <></>
@@ -22,7 +22,7 @@ const SystemLookupCombobox: React.FC<SystemLookupListProps> = (props) => {
22
22
 
23
23
  const loadLookupOptions = async () => {
24
24
  await handleGetRequest({
25
- endPointURI: "api/v1/dev/system/lookup",
25
+ endPointURI: "api/v1/public/system/lookup",
26
26
  showMask: true,
27
27
  parameters: { lookupType: props.lookupType },
28
28
  successCallBkFn: (response: any) => {
@@ -4,7 +4,7 @@ import * as z from "zod";
4
4
  import { useForm } from "react-hook-form";
5
5
  import { zodResolver } from "@hookform/resolvers/zod";
6
6
  import { useParams } from "react-router-dom";
7
- import { Button, Grid2 } from "@mui/material";
7
+ import { Button, Grid2, Icon, IconButton, Tooltip } from "@mui/material";
8
8
  import { toast } from "react-toastify";
9
9
  import FormElementGroup from "./FormElementGroup";
10
10
  import FormElementField from "./FormElementField";
@@ -17,10 +17,33 @@ import {
17
17
  } from "../DataEntryTypes";
18
18
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
19
19
  import { useTranslation } from "react-i18next";
20
+ import { useWindow } from "../../../../hooks";
21
+ import AttachmentPanel from "../../attachment/AttachmentPanel";
22
+ import WorkflowDocumentPanel from "../../workflow/WorkflowDocumentPanel";
20
23
 
21
24
  const TemplateForm: React.FC<TemplateFormProps> = (
22
25
  props: TemplateFormProps
23
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);
24
47
  const { t } = useTranslation();
25
48
  const fields = getAllFields(props.elements);
26
49
  const initiallyHiddenFields = [];
@@ -141,8 +164,47 @@ const TemplateForm: React.FC<TemplateFormProps> = (
141
164
  loadRecord();
142
165
  }, [props?.recordIdToEdit]);
143
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
+
144
177
  return (
145
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
+
146
208
  <Box
147
209
  sx={{
148
210
  display: "flex",
@@ -218,14 +280,44 @@ const TemplateForm: React.FC<TemplateFormProps> = (
218
280
  justifyContent: "flex-start",
219
281
  }}
220
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}
221
309
  {props?.actions ? (
222
- props.actions.map((action: RecordAction) => {
223
- if (action?.formActionProps?.enabled === true) {
224
- return <FormAction {...action} record={formValues} />;
225
- } else {
226
- return <></>;
227
- }
228
- })
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
+ )
229
321
  ) : (
230
322
  <></>
231
323
  )}
@@ -47,6 +47,8 @@ import { useWindow } from "../../../../hooks/UseWindow";
47
47
  import { useAxios } from "../../../../hooks";
48
48
  import useLookupGridColumn from "../../../../hooks/useLookupGridColumn";
49
49
  import { ContinuousColorLegend } from "@mui/x-charts";
50
+ import AttachmentPanel from "../../attachment/AttachmentPanel";
51
+ import WorkflowDocumentPanel from "../../workflow/WorkflowDocumentPanel";
50
52
  let currentNewRecordIndex = -1;
51
53
 
52
54
  interface GridState {
@@ -78,12 +80,33 @@ const saveGridState = (gridStateKey: string, statePart: GridState) => {
78
80
  const PIN_FIXED_COLUMNS = ["__check__", "actions"];
79
81
 
80
82
  const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
83
+ const { t } = useTranslation();
81
84
  const AppLayout = useSelector((state: any) => state.AppLayout);
85
+ const [selectedRecord, setSelectedRecord] = useState<any>({});
86
+ const [attachmentPanelEnabledForRecord, setAttachmentPanelEnabledForRecord] =
87
+ useState<boolean>(true);
82
88
  const { getLookupOptions } = useLookupGridColumn();
89
+ const { Window: AttachmentWindow, setWindowState: setAttachmentWindowState } =
90
+ useWindow({
91
+ windowTitle: t(props.gridTitle) + " Attachments",
92
+ windowIcon: "paperclip",
93
+ width: "fit-content",
94
+ height: "fit-content",
95
+ minHeight: 500,
96
+ minWidth: 400,
97
+ });
98
+ const { Window: WorkFlowWindow, setWindowState: setWorkFlowWindowState } =
99
+ useWindow({
100
+ windowTitle: t(props.gridTitle) + " Attachments",
101
+ windowIcon: "paperclip",
102
+ width: "fit-content",
103
+ height: "fit-content",
104
+ minHeight: 500,
105
+ minWidth: 400,
106
+ });
83
107
  const [generatedColumns, setGeneratedColumns] = useState<
84
108
  Array<TemplateGridColDef>
85
109
  >([]);
86
- const { t } = useTranslation();
87
110
  const fields = getAllFields(props.formElements);
88
111
  const hiddenFields = [];
89
112
  const savedState = React.useMemo<GridState>(
@@ -174,7 +197,7 @@ const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
174
197
  gridColumn.options = await getLookupOptions(gridColumn.lookupType);
175
198
  gridColumn.valueField = "lookupValue";
176
199
  }
177
- }
200
+ }
178
201
  setGeneratedColumns(gridColumns);
179
202
  };
180
203
  const [rowSelectionModel, setRowSelectionModel] =
@@ -558,6 +581,57 @@ const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
558
581
  }
559
582
  }
560
583
  }
584
+ if (
585
+ record[props?.keyColumnName || "id"] &&
586
+ record[props?.keyColumnName || "id"] > 0 &&
587
+ props?.attachment
588
+ ) {
589
+ actions?.push(
590
+ <GridActionsCellItem
591
+ icon={
592
+ <Tooltip title={"Attachments"}>
593
+ <FontAwesomeIcon icon={"paperclip"} />
594
+ </Tooltip>
595
+ }
596
+ label={"Attachments"}
597
+ className="textPrimary"
598
+ color="inherit"
599
+ onClick={() => {
600
+ setSelectedRecord(record);
601
+ if (props?.attachment?.enableAttachFn) {
602
+ setAttachmentPanelEnabledForRecord(
603
+ props.attachment.enableAttachFn(record)
604
+ );
605
+ } else {
606
+ setAttachmentPanelEnabledForRecord(true);
607
+ }
608
+ setAttachmentWindowState(true);
609
+ }}
610
+ />
611
+ );
612
+ }
613
+ if (
614
+ record[props?.keyColumnName || "id"] &&
615
+ record[props?.keyColumnName || "id"] > 0 &&
616
+ props?.workFlowDocumentCode
617
+ ) {
618
+ actions?.push(
619
+ <GridActionsCellItem
620
+ icon={
621
+ <Tooltip title={"Approvals"}>
622
+ <FontAwesomeIcon icon={"stamp"} />
623
+ </Tooltip>
624
+ }
625
+ label={"Approvals"}
626
+ className="textPrimary"
627
+ color="inherit"
628
+ onClick={() => {
629
+ setSelectedRecord(record);
630
+ setWorkFlowWindowState(true);
631
+ }}
632
+ />
633
+ );
634
+ }
561
635
  if (props?.rowActions) {
562
636
  for (const rowAction of props.rowActions) {
563
637
  if (
@@ -590,7 +664,9 @@ const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
590
664
  width:
591
665
  (props?.rowActions ? props.rowActions.length * 30 : 0) +
592
666
  (props?.editAction && props?.editAction?.isEnabled ? 30 : 0) +
593
- (props?.deleteAction && props?.deleteAction?.isEnabled ? 30 : 0),
667
+ (props?.deleteAction && props?.deleteAction?.isEnabled ? 30 : 0) +
668
+ (props?.attachment ? 30 : 0) +
669
+ (props?.workFlowDocumentCode ? 30 : 0),
594
670
  getActions: getActionColumnActions,
595
671
  };
596
672
 
@@ -614,6 +690,10 @@ const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
614
690
  adjustGridColumns();
615
691
  }, []);
616
692
 
693
+ useEffect(() => {
694
+ props.apiActions.reloadData(props?.gridLoadParametersValues);
695
+ }, [session.UserInfo?.currentOrganization]);
696
+
617
697
  useEffect(() => {
618
698
  adjustGridColumns();
619
699
  }, [themeDirection, props.formElements]);
@@ -698,6 +778,8 @@ const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
698
778
  />
699
779
  ) : (
700
780
  <TemplateForm
781
+ keyColumnName={props.keyColumnName}
782
+ attachment={props.attachment}
701
783
  formLoadCallBk={props.formLoadCallBk}
702
784
  recordIdToEdit={
703
785
  recordToEdit ? recordToEdit[keyColumnName] : undefined
@@ -818,7 +900,34 @@ const TemplateGrid: React.FC<TemplateGridProps> = (props) => {
818
900
  ) : (
819
901
  <></>
820
902
  )}
821
-
903
+ {props?.attachment ? (
904
+ <AttachmentWindow>
905
+ <AttachmentPanel
906
+ attachmentCode={props.attachment.attachmentCode}
907
+ refKey={selectedRecord[props?.keyColumnName || "id"]}
908
+ enableAttachment={attachmentPanelEnabledForRecord}
909
+ />
910
+ </AttachmentWindow>
911
+ ) : (
912
+ <></>
913
+ )}
914
+ {props?.workFlowDocumentCode ? (
915
+ <WorkFlowWindow>
916
+ <WorkflowDocumentPanel
917
+ workFlowDocumentCode={props.workFlowDocumentCode}
918
+ refDocumentId={selectedRecord[props?.keyColumnName || "id"]}
919
+ postActionCallBk={() => {
920
+ setWorkFlowWindowState(false);
921
+ props.apiActions.reloadData(props.gridLoadParametersValues);
922
+ }}
923
+ cancelActionCallBk={() => {
924
+ setWorkFlowWindowState(false);
925
+ }}
926
+ />
927
+ </WorkFlowWindow>
928
+ ) : (
929
+ <></>
930
+ )}
822
931
  <DataGridPremium
823
932
  {...props?.muiProps}
824
933
  slots={{ toolbar: TemplateGridTopBar }}
@@ -0,0 +1,126 @@
1
+ import Card from "@mui/material/Card";
2
+ import CardActions from "@mui/material/CardActions";
3
+ import CardContent from "@mui/material/CardContent";
4
+ import CardMedia from "@mui/material/CardMedia";
5
+ import Button from "@mui/material/Button";
6
+ import Typography from "@mui/material/Typography";
7
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
8
+ import { IconButton, Tooltip } from "@mui/material";
9
+ import { useAxios, useConfirmationWindow } from "../../../hooks";
10
+ import AttachmentImageViewer from "./AttachmentImageViewer";
11
+
12
+ export type attachment = {
13
+ attachmentCode: string;
14
+ setAttachmentConfig: (props: any) => void;
15
+ id: number;
16
+ attachmentConfigId: number;
17
+ attachmentSize: number;
18
+ category: string;
19
+ docType: string;
20
+ fileName: string;
21
+ refKey: string;
22
+ remark: string;
23
+ };
24
+
25
+ const AttachmentCard: React.FC<attachment> = (props) => {
26
+ const { handleGetRequest, handleDeleteRequest } = useAxios();
27
+
28
+ const handleDelete = async () => {
29
+ await handleDeleteRequest({
30
+ endPointURI: "api/v1/attachment/archive",
31
+ parameters: {
32
+ attachmentCode: props.attachmentCode,
33
+ refKey: props.refKey,
34
+ attachmentId: props.id,
35
+ },
36
+ successCallBkFn: (response) => {
37
+ props.setAttachmentConfig(response.data);
38
+ },
39
+ showMask: true,
40
+ });
41
+ };
42
+ const { ConfirmationWindow, setOpen } = useConfirmationWindow({
43
+ title: "Confirmation",
44
+ body: "Are you sure you want to delete this attachment?",
45
+ onConfirmationCallBk: () => {
46
+ handleDelete();
47
+ },
48
+ });
49
+
50
+ const handleDownload = async () => {
51
+ await handleGetRequest({
52
+ endPointURI: "api/v1/attachment/download",
53
+ parameters: {
54
+ attachmentId: props.id,
55
+ },
56
+ showMask: true,
57
+ responseType: "blob",
58
+ successCallBkFn: (response) => {
59
+ const url = window.URL.createObjectURL(new Blob([response.data]));
60
+ const link = document.createElement("a");
61
+ link.href = url;
62
+ link.setAttribute("download", props.fileName);
63
+ document.body.appendChild(link);
64
+ link.click();
65
+ link.remove();
66
+ },
67
+ });
68
+ };
69
+
70
+ return (
71
+ <>
72
+ <ConfirmationWindow />
73
+ <Card sx={{ width: 300, border: "0.5px solid gray" }}>
74
+ <CardMedia
75
+ sx={{
76
+ height: 140,
77
+ width: 300,
78
+ display: "flex",
79
+ alignItems: "center",
80
+ justifyContent: "center",
81
+ }}
82
+ >
83
+ {props?.docType.toLocaleLowerCase().includes("image") ? (
84
+ <AttachmentImageViewer attachmentId={props.id} />
85
+ ) : (
86
+ <FontAwesomeIcon icon="file" size="3x" />
87
+ )}
88
+ </CardMedia>
89
+ <CardContent>
90
+ <Typography gutterBottom variant="h6" component="div">
91
+ {props.fileName}
92
+ </Typography>
93
+ <Typography variant="body2" sx={{ color: "text.secondary" }}>
94
+ {`File Size: ${props?.attachmentSize || "unknown"} kb`}
95
+ </Typography>
96
+ <Typography variant="body2" sx={{ color: "text.secondary" }}>
97
+ {`File Type: ${props?.category || "NA"}`}
98
+ </Typography>
99
+ <Typography variant="body2" sx={{ color: "text.secondary" }}>
100
+ {props.remark}
101
+ </Typography>
102
+ </CardContent>
103
+ <CardActions>
104
+ <IconButton
105
+ size="small"
106
+ onClick={() => {
107
+ setOpen(true);
108
+ }}
109
+ >
110
+ <Tooltip title="Delete Attachment">
111
+ <FontAwesomeIcon icon="trash" />
112
+ </Tooltip>
113
+ </IconButton>
114
+ <div style={{ flex: 1 }}></div>
115
+ <IconButton size="small" onClick={handleDownload}>
116
+ <Tooltip title="Download Attachment">
117
+ <FontAwesomeIcon icon="download" />
118
+ </Tooltip>
119
+ </IconButton>
120
+ </CardActions>
121
+ </Card>
122
+ </>
123
+ );
124
+ };
125
+
126
+ export default AttachmentCard;
@@ -0,0 +1,44 @@
1
+ import { useState } from "react";
2
+ import { useSelector } from "react-redux";
3
+ import { Avatar } from "@mui/material";
4
+
5
+ type AttachmentImageViewerProps = {
6
+ attachmentId?: number;
7
+ attachmentCode?: string;
8
+ refKey?: string;
9
+ category?: string;
10
+ showAsAvatar?: boolean;
11
+ };
12
+
13
+ const AttachmentImageViewer: React.FC<AttachmentImageViewerProps> = (props) => {
14
+ const apiBaseUrl = useSelector(
15
+ (state: any) => state.AppInfo.value.apiBaseUrl
16
+ );
17
+ let imagePath = apiBaseUrl + "/api/v1/attachment/";
18
+ if (props?.attachmentId) {
19
+ imagePath += "download?attachmentId=" + props.attachmentId;
20
+ } else {
21
+ imagePath += `downloadImage?attachmentCode=${props.attachmentCode}&refKey=${props.refKey}`;
22
+ if (props?.category) {
23
+ imagePath += `&category=${props.category}`;
24
+ }
25
+ }
26
+ const [imgSrc, setImgSrc] = useState(imagePath);
27
+ return props?.showAsAvatar ? (
28
+ <Avatar src={imgSrc} />
29
+ ) : (
30
+ <img
31
+ src={imgSrc}
32
+ alt="image"
33
+ onError={() => setImgSrc("/no_image.png")}
34
+ style={{
35
+ width: "100%",
36
+ height: "100%",
37
+ objectFit: "cover",
38
+ display: "block",
39
+ }}
40
+ />
41
+ );
42
+ };
43
+
44
+ export default AttachmentImageViewer;