umwd-components 0.1.686 → 0.1.688

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 (29) hide show
  1. package/dist/src/components/e-commerce/products/EditStockForm.js +1 -1
  2. package/dist/src/components/logistics/note/NoteTakingComponent.js +1 -1
  3. package/dist/src/components/logistics/report/ReportMakingComponent.js +1 -1
  4. package/dist/src/components/logistics/report/ReportsDisplay.js +1 -1
  5. package/dist/src/data/actions/logistics/note/createNoteAction.js +1 -1
  6. package/dist/src/data/actions/logistics/report/createReportAction.js +1 -1
  7. package/dist/src/data/loaders/e-commerce/getAllOpos.js +1 -1
  8. package/dist/src/data/loaders/e-commerce/getPaginatedCategories.js +1 -1
  9. package/dist/src/data/loaders/user/getUserMeLoader.js +7 -0
  10. package/dist/tsconfig.build.tsbuildinfo +1 -1
  11. package/dist/types/components/logistics/report/ReportMakingComponent.d.ts +1 -1
  12. package/dist/types/data/actions/logistics/report/createReportAction.d.ts +1 -1
  13. package/dist/types/data/loaders/e-commerce/getAllOpos.d.ts +1 -1
  14. package/dist/types/data/loaders/e-commerce/getPaginatedCategories.d.ts +1 -1
  15. package/dist/types/data/loaders/user/getUserMeLoader.d.ts +10 -0
  16. package/dist/types/types/logistics/Report.d.ts +3 -0
  17. package/dist/types/types/user.d.ts +14 -0
  18. package/package.json +1 -1
  19. package/src/components/e-commerce/products/EditStockForm.tsx +15 -9
  20. package/src/components/logistics/note/NoteTakingComponent.tsx +28 -23
  21. package/src/components/logistics/report/ReportMakingComponent.tsx +74 -14
  22. package/src/components/logistics/report/ReportsDisplay.tsx +48 -7
  23. package/src/data/actions/logistics/note/createNoteAction.ts +3 -0
  24. package/src/data/actions/logistics/report/createReportAction.ts +3 -1
  25. package/src/data/loaders/e-commerce/getAllOpos.ts +1 -1
  26. package/src/data/loaders/e-commerce/getPaginatedCategories.ts +9 -2
  27. package/src/data/loaders/user/getUserMeLoader.ts +49 -0
  28. package/src/types/logistics/Report.ts +3 -0
  29. package/src/types/user.ts +15 -0
@@ -2,4 +2,4 @@ import React from "react";
2
2
  import { ReportMakingComponentProps } from "../../../types/logistics/Report";
3
3
  /** INFO Reports can only be created with entities that already excist,
4
4
  * this is because the report has to be saved with a relation to the parent entity */
5
- export default function ReportMakingComponent({ content, related, type, minValue, maxValue, revalidateCallback, }: ReportMakingComponentProps): React.JSX.Element;
5
+ export default function ReportMakingComponent({ content, related, type, minValue, maxValue, revalidateCallback, biggerButton, }: ReportMakingComponentProps): React.JSX.Element;
@@ -1 +1 @@
1
- export declare function createReportAction(related: any, prevState: any, formData: FormData): Promise<any>;
1
+ export declare function createReportAction(related: any, type: string, prevState: any, formData: FormData): Promise<any>;
@@ -1,2 +1,2 @@
1
- declare function getAllOpos(currentPage: number, rowsPerPage: number, order: "asc" | "desc", orderBy: string, is_archive: boolean[]): Promise<any>;
1
+ declare function getAllOpos(currentPage: number, rowsPerPage: number, order: "asc" | "desc", orderBy: string, is_archive?: boolean[]): Promise<any>;
2
2
  export { getAllOpos };
@@ -1 +1 @@
1
- export declare function getPaginatedCategories(currentPage?: number, rowsPerPage?: number, order?: "asc" | "desc", orderBy?: string): Promise<any>;
1
+ export declare function getPaginatedCategories(currentPage?: number, rowsPerPage?: number, order?: "asc" | "desc", orderBy?: string, is_archive?: boolean[]): Promise<any>;
@@ -0,0 +1,10 @@
1
+ import { User } from "@/types/user";
2
+ export declare function getUserMeLoader(query?: string): Promise<{
3
+ ok: boolean;
4
+ data: null;
5
+ error: any;
6
+ } | {
7
+ ok: boolean;
8
+ data: User;
9
+ error: null;
10
+ }>;
@@ -16,6 +16,8 @@ export type Report = {
16
16
  type: ReportType;
17
17
  related: PolymorphicRelation[];
18
18
  author: any;
19
+ signature_username?: string;
20
+ signature_email?: string;
19
21
  };
20
22
  export interface ReportMakingComponentProps {
21
23
  author?: string;
@@ -27,6 +29,7 @@ export interface ReportMakingComponentProps {
27
29
  type: ReportType;
28
30
  related?: PolymorphicRelation[];
29
31
  revalidateCallback?: () => void;
32
+ biggerButton?: boolean;
30
33
  }
31
34
  export interface SimpleReportFieldsProps {
32
35
  itemID: number;
@@ -0,0 +1,14 @@
1
+ import { CustomerProfileProps } from "./e-commerce/customer/types";
2
+ import { EnduserProfileProps } from "./e-commerce/enduser/types";
3
+ import { DispatcherProfileProps } from "./logistics/dispatcher/types";
4
+ export type User = {
5
+ id: string;
6
+ username: string;
7
+ email: string;
8
+ role?: "customer" | "enduser" | "dispatcher";
9
+ created_at: Date;
10
+ updated_at: Date;
11
+ customer_profile?: CustomerProfileProps;
12
+ enduser_profile?: EnduserProfileProps;
13
+ dispatcher_profile?: DispatcherProfileProps;
14
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "umwd-components",
3
- "version": "0.1.686",
3
+ "version": "0.1.688",
4
4
  "description": "UMWD Component library",
5
5
  "main": "dist/src/index.js",
6
6
  "module": "dist/src/index.js",
@@ -26,6 +26,7 @@ import Button from "@mui/material/Button";
26
26
  import AmountIndicator from "../../../components/common/AmountIndicator";
27
27
  import { IpoItem } from "../../../types/logistics/Ipo";
28
28
  import { OpoItem } from "../../../types/e-commerce/opo/types";
29
+ import { alignProperty } from "@mui/material/styles/cssUtils";
29
30
 
30
31
  const INITIAL_STATE = {
31
32
  zodErrors: null,
@@ -384,19 +385,22 @@ export function EditStockForm({
384
385
  not forget to click the button!
385
386
  </Typography>
386
387
  <ReportsDisplay reports={reports} />
387
- <ReportMakingComponent
388
- content={"content"}
389
- quantity={0}
390
- type="stock"
391
- related={[{ id: id, __type: "api::e-commerce.product" }]}
392
- revalidateCallback={revalidateCallback}
393
- />
388
+ <Stack direction="row" justifyContent={"flex-end"}>
389
+ <ReportMakingComponent
390
+ content={"content"}
391
+ quantity={0}
392
+ type="stock"
393
+ related={[{ id: id, __type: "api::e-commerce.product" }]}
394
+ revalidateCallback={revalidateCallback}
395
+ biggerButton={true}
396
+ />
397
+ </Stack>
394
398
  </Stack>
395
399
  </Grid>
396
400
  <Grid item xs={12}>
397
401
  <Stack
398
402
  direction="row"
399
- justifyContent="space-between"
403
+ justifyContent="flex-end"
400
404
  alignItems="center"
401
405
  sx={{ py: 1 }}
402
406
  >
@@ -405,11 +409,13 @@ export function EditStockForm({
405
409
  Close
406
410
  </Button>
407
411
  )}
412
+ {/*
413
+ Nothing to submit here, the form is just for the reports
408
414
  <SubmitButton
409
415
  text="Update Stock"
410
416
  loadingText="Updating..."
411
417
  variant="contained"
412
- />
418
+ /> */}
413
419
  </Stack>
414
420
  </Grid>
415
421
  </Grid>
@@ -17,12 +17,14 @@ import { useFormState } from "react-dom";
17
17
  import { createNoteAction } from "../../../data/actions/logistics/note/createNoteAction";
18
18
  import { NoteTakingComponentProps } from "../../../types/logistics/Note";
19
19
  import MarkdownEditor from "../../../components/common/markdown/MarkdownEditor";
20
+ import { useSnackbar } from "../../../context/common/SnackbarContext";
20
21
 
21
22
  const INITIAL_STATE = {
22
23
  zodErrors: null,
23
24
  strapiErrors: null,
24
25
  data: null,
25
26
  message: null,
27
+ severity: null,
26
28
  };
27
29
 
28
30
  /** INFO Notes can only be created with entities that already excist,
@@ -41,11 +43,28 @@ export default function NoteTakingComponent({
41
43
  INITIAL_STATE
42
44
  );
43
45
 
46
+ const { handleAddMessage } = useSnackbar();
47
+
44
48
  useEffect(() => {
45
- if (formState?.data && revalidateCallback) {
46
- revalidateCallback();
49
+ if (formState?.strapiErrors) {
50
+ handleAddMessage({
51
+ message: formState.strapiErrors,
52
+ severity: formState.severity || "error",
53
+ });
54
+ }
55
+ if (formState?.message) {
56
+ handleAddMessage({
57
+ message: formState.message,
58
+ severity: formState.severity || "success",
59
+ });
60
+ if (formState?.severity === "success") {
61
+ setOpen(false);
62
+ if (revalidateCallback) {
63
+ revalidateCallback();
64
+ }
65
+ }
47
66
  }
48
- }, [formState.data]);
67
+ }, [formState]);
49
68
 
50
69
  return (
51
70
  <>
@@ -60,7 +79,12 @@ export default function NoteTakingComponent({
60
79
  <NoteAltIcon fontSize="large" />
61
80
  </IconButton>
62
81
 
63
- <Dialog open={open} fullWidth={true} maxWidth={"lg"}>
82
+ <Dialog
83
+ open={open}
84
+ fullWidth={true}
85
+ maxWidth={"lg"}
86
+ onClose={() => setOpen(false)}
87
+ >
64
88
  <form action={formAction}>
65
89
  <DialogContent sx={{ p: 1 }}>
66
90
  <Paper
@@ -75,22 +99,7 @@ export default function NoteTakingComponent({
75
99
  <Stack spacing={2}>
76
100
  <Typography variant="body1" textAlign={"right"}>
77
101
  {dayjs(Date.now()).format("DD-MM-YYYY")}
78
- {/* format(Date.now(), "dd-MM-yyyy")} */}
79
102
  </Typography>
80
- {/* <TextField
81
- id="content"
82
- name="content"
83
- sx={{
84
- color: "black",
85
- label: { color: "black" },
86
- input: { color: "black" },
87
- div: { color: "black" },
88
- }}
89
- label="The note"
90
- multiline
91
- rows={12}
92
- defaultValue={content}
93
- /> */}
94
103
  <MarkdownEditor
95
104
  name={"content"}
96
105
  label="The note"
@@ -108,10 +117,6 @@ export default function NoteTakingComponent({
108
117
  <DialogActions>
109
118
  <Button onClick={() => setOpen(false)}>Cancel</Button>
110
119
  <SubmitButton text="Submit" loadingText="Loading..." />
111
- <StrapiErrors error={formState?.strapiErrors} />
112
- {formState?.message && (
113
- <Alert severity="error">{formState?.message}</Alert>
114
- )}
115
120
  </DialogActions>
116
121
  </form>
117
122
  </Dialog>
@@ -19,7 +19,8 @@ import { ReportMakingComponentProps } from "../../../types/logistics/Report";
19
19
  import MarkdownEditor from "../../../components/common/markdown/MarkdownEditor";
20
20
  import AnnouncementIcon from "@mui/icons-material/Announcement";
21
21
  import { Box, Tooltip } from "@mui/material";
22
- import { Block } from "@mui/icons-material";
22
+ import { getUserMeLoader } from "../../../data/loaders/user/getUserMeLoader";
23
+ import { useSnackbar } from "../../../context/common/SnackbarContext";
23
24
 
24
25
  const INITIAL_STATE = {
25
26
  zodErrors: null,
@@ -37,12 +38,21 @@ export default function ReportMakingComponent({
37
38
  minValue,
38
39
  maxValue,
39
40
  revalidateCallback,
41
+ biggerButton = false, // if true, the button will be bigger
40
42
  }: ReportMakingComponentProps) {
41
43
  const [open, setOpen] = useState(false);
42
44
 
45
+ interface User {
46
+ username: string;
47
+ email: string;
48
+ }
49
+
50
+ const [currentUser, setCurrentUser] = useState<User | null>(null);
51
+
43
52
  const createReportWithRelationsAction = createReportAction.bind(
44
53
  null,
45
- related
54
+ related,
55
+ type
46
56
  );
47
57
 
48
58
  const [formState, formAction] = useFormState(
@@ -50,27 +60,67 @@ export default function ReportMakingComponent({
50
60
  INITIAL_STATE
51
61
  );
52
62
 
63
+ const { handleAddMessage } = useSnackbar();
64
+
65
+ useEffect(() => {
66
+ if (formState?.message) {
67
+ handleAddMessage({
68
+ message: formState.message,
69
+ severity: formState.severity || "error",
70
+ });
71
+
72
+ if (formState.severity === "success") {
73
+ revalidateCallback && revalidateCallback();
74
+ setOpen(false);
75
+ }
76
+ }
77
+ }, [formState?.message]);
78
+
53
79
  useEffect(() => {
54
- if (formState?.data && revalidateCallback) {
55
- revalidateCallback();
80
+ async function fetchUser() {
81
+ const { ok, data, error } = await getUserMeLoader();
82
+ if (ok && data) {
83
+ console.log("Current User:", data);
84
+ setCurrentUser(data);
85
+ } else {
86
+ setCurrentUser(null);
87
+ console.error("Error fetching current user:", error);
88
+ }
56
89
  }
57
- }, [formState.data]);
90
+ fetchUser();
91
+ }, []);
58
92
 
59
93
  return (
60
94
  <>
61
95
  <Box sx={{ display: "block", position: "relative" }}>
62
- <Tooltip title="Create a report">
63
- <IconButton onClick={() => setOpen(true)}>
64
- <AnnouncementIcon fontSize="large" />
65
- </IconButton>
66
- </Tooltip>
96
+ {biggerButton ? (
97
+ <Button
98
+ variant="contained"
99
+ onClick={() => setOpen(true)}
100
+ startIcon={<AnnouncementIcon />}
101
+ >
102
+ Create Report
103
+ </Button>
104
+ ) : (
105
+ <Tooltip title="Create a report">
106
+ <IconButton onClick={() => setOpen(true)}>
107
+ <AnnouncementIcon fontSize="large" />
108
+ </IconButton>
109
+ </Tooltip>
110
+ )}
67
111
  </Box>
68
112
 
69
- <Dialog open={open} fullWidth={true} maxWidth={"lg"}>
113
+ <Dialog
114
+ open={open}
115
+ fullWidth={true}
116
+ maxWidth={"lg"}
117
+ sx={{ marginX: 1 }}
118
+ onClose={() => setOpen(false)}
119
+ >
70
120
  <form action={formAction}>
71
121
  <DialogContent sx={{ p: 1 }}>
122
+ {" "}
72
123
  <Stack spacing={2}>
73
- <input type="hidden" name="type" value={type} />
74
124
  <Typography variant="body1" textAlign={"right"}>
75
125
  {dayjs(Date.now()).format("DD-MM-YYYY")}
76
126
  </Typography>
@@ -111,15 +161,25 @@ export default function ReportMakingComponent({
111
161
  label="The report"
112
162
  defaultValue={content}
113
163
  />
164
+ {currentUser !== null && (
165
+ <>
166
+ <Typography variant="body1" textAlign={"right"}>
167
+ {currentUser.username}
168
+ </Typography>
169
+ <Typography variant="body2" textAlign={"right"}>
170
+ {currentUser.email}
171
+ </Typography>
172
+ </>
173
+ )}
114
174
  </Stack>
115
175
  </DialogContent>
116
176
  <DialogActions>
117
177
  <Button onClick={() => setOpen(false)}>Cancel</Button>
118
178
  <SubmitButton text="Submit" loadingText="Loading..." />
119
- <StrapiErrors error={formState?.strapiErrors} />
179
+ {/* <StrapiErrors error={formState?.strapiErrors} />
120
180
  {formState?.message && (
121
181
  <Alert severity="error">{formState?.message}</Alert>
122
- )}
182
+ )} */}
123
183
  </DialogActions>
124
184
  </form>
125
185
  </Dialog>
@@ -15,23 +15,64 @@ const ReportsDisplay = ({ reports }: { reports: Report[] }) => {
15
15
  reports.map((report, index) => {
16
16
  return (
17
17
  <Grid container key={index}>
18
- <Grid item xs={1}>
18
+ <Grid
19
+ item
20
+ xs={1}
21
+ sx={{
22
+ display: "flex",
23
+ alignItems: "center",
24
+ justifyContent: "flex-start",
25
+ }}
26
+ >
19
27
  <Chip
20
28
  label={`${report.quantity > 0 ? "+" : ""} ${report.quantity}`}
21
29
  color={report.quantity > 0 ? "success" : "error"}
22
30
  size="small"
23
31
  />
24
32
  </Grid>
25
- <Grid item xs={7}>
33
+ <Grid
34
+ item
35
+ xs={7}
36
+ sx={{
37
+ display: "flex",
38
+ alignItems: "center",
39
+ justifyContent: "flex-start",
40
+ }}
41
+ >
26
42
  <MarkdownDisplay>{report.content}</MarkdownDisplay>
27
43
  </Grid>
28
- <Grid item xs={2}>
44
+ <Grid
45
+ item
46
+ xs={2}
47
+ sx={{
48
+ display: "flex",
49
+ alignItems: "center",
50
+ justifyContent: "flex-start",
51
+ }}
52
+ >
29
53
  <Typography variant="body2"> {report.date}</Typography>
30
54
  </Grid>
31
- <Grid item xs={2}>
32
- <Typography variant="body2" align="right">
33
- {report.author?.username ? report.author.username : "Unknown"}
34
- </Typography>
55
+ <Grid
56
+ item
57
+ xs={2}
58
+ sx={{
59
+ display: "flex",
60
+ alignItems: "center",
61
+ justifyContent: "flex-start",
62
+ }}
63
+ >
64
+ <Stack direction="column" alignItems={"flex-end"}>
65
+ {report.signature_username && (
66
+ <Typography variant="body1">
67
+ {report.signature_username}
68
+ </Typography>
69
+ )}
70
+ {report.signature_username && (
71
+ <Typography variant="body2">
72
+ {report.signature_email}
73
+ </Typography>
74
+ )}
75
+ </Stack>
35
76
  </Grid>
36
77
 
37
78
  <Grid item xs={12}>
@@ -28,6 +28,7 @@ export async function createNoteAction(
28
28
  ...prevState,
29
29
  strapiErrors: null,
30
30
  message: "Ops! Something went wrong. Please try again.",
31
+ severity: "error",
31
32
  };
32
33
  }
33
34
 
@@ -36,6 +37,7 @@ export async function createNoteAction(
36
37
  ...prevState,
37
38
  strapiErrors: responseData.error,
38
39
  message: "Failed to Create Note.",
40
+ severity: "error",
39
41
  };
40
42
  }
41
43
 
@@ -44,6 +46,7 @@ export async function createNoteAction(
44
46
  return {
45
47
  ...prevState,
46
48
  message: "New Note Created",
49
+ severity: "success",
47
50
  data: flattenedData,
48
51
  strapiErrors: null,
49
52
  };
@@ -7,15 +7,16 @@ import { parseFormData } from "../../../../lib/parseFormData";
7
7
 
8
8
  export async function createReportAction(
9
9
  related: any,
10
+ type: string,
10
11
  prevState: any,
11
12
  formData: FormData
12
13
  ) {
13
14
  const parsedFormData = parseFormData(formData);
14
-
15
15
  const reportData = {
16
16
  data: {
17
17
  ...parsedFormData.data,
18
18
  related,
19
+ type,
19
20
  },
20
21
  };
21
22
 
@@ -46,6 +47,7 @@ export async function createReportAction(
46
47
  return {
47
48
  ...prevState,
48
49
  message: "New Report Created",
50
+ severity: "success",
49
51
  data: flattenedData,
50
52
  strapiErrors: null,
51
53
  };
@@ -13,7 +13,7 @@ async function getAllOpos(
13
13
  rowsPerPage: number,
14
14
  order: "asc" | "desc",
15
15
  orderBy: string,
16
- is_archive: boolean[]
16
+ is_archive: boolean[] = [false] // default to both archived and non-archived OPOs
17
17
  ) {
18
18
  noStore();
19
19
 
@@ -10,7 +10,8 @@ export async function getPaginatedCategories(
10
10
  currentPage?: number,
11
11
  rowsPerPage?: number,
12
12
  order?: "asc" | "desc",
13
- orderBy?: string
13
+ orderBy?: string,
14
+ is_archive: boolean[] = [false] // default to both archived and non-archived categories
14
15
  ) {
15
16
  const url = new URL(`/api/product-categories`, baseUrl);
16
17
 
@@ -19,10 +20,16 @@ export async function getPaginatedCategories(
19
20
  currentPage,
20
21
  rowsPerPage,
21
22
  order,
22
- orderBy
23
+ orderBy,
24
+ is_archive
23
25
  );
24
26
 
25
27
  url.search = qs.stringify({
28
+ filters: {
29
+ is_archive: {
30
+ $in: is_archive,
31
+ },
32
+ },
26
33
  populate: {
27
34
  products: {
28
35
  populate: {
@@ -0,0 +1,49 @@
1
+ "use server";
2
+
3
+ import { getAuthToken } from "../../services/get-token";
4
+ import qs from "qs";
5
+ import { flattenAttributes, getStrapiURL } from "../../../lib/utils";
6
+ import { unstable_noStore as noStore } from "next/cache";
7
+ import { User } from "@/types/user";
8
+
9
+ const baseUrl = getStrapiURL();
10
+
11
+ const defaultQuery = qs.stringify({
12
+ fields: ["id", "username", "email", "createdAt", "updatedAt"],
13
+ });
14
+
15
+ export async function getUserMeLoader(query: string = defaultQuery) {
16
+ noStore();
17
+ const url = new URL("/api/users/me", baseUrl);
18
+ url.search = query;
19
+
20
+ const authToken = await getAuthToken();
21
+ if (!authToken) return { ok: false, data: null, error: null };
22
+
23
+ try {
24
+ const response = await fetch(url.href, {
25
+ method: "GET",
26
+ headers: {
27
+ "Content-Type": "application/json",
28
+ Authorization: `Bearer ${authToken}`,
29
+ },
30
+ cache: "no-cache", // Ensure the response is not cached and revalidated on every render
31
+ });
32
+
33
+ // Check if the response status is not OK (i.e., not in the range 200-299)
34
+ if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
35
+
36
+ const data = await response.json();
37
+
38
+ // Check if the data contains an error field
39
+ if (data.error) return { ok: false, data: null, error: data.error };
40
+
41
+ // If everything is fine, return the data
42
+ // return { ok: true, data: data, error: null };
43
+ const user: User = flattenAttributes(data);
44
+ return { ok: true, data: user, error: null };
45
+ } catch (error) {
46
+ console.error("getUserMeLoader Error:", error);
47
+ return { ok: false, data: null, error: error };
48
+ }
49
+ }
@@ -28,6 +28,8 @@ export type Report = {
28
28
  type: ReportType;
29
29
  related: PolymorphicRelation[];
30
30
  author: any;
31
+ signature_username?: string; // name of the person who made the report, should be set by the cms
32
+ signature_email?: string; // email of the person who made the report, should be set by the cms
31
33
  };
32
34
 
33
35
  export interface ReportMakingComponentProps {
@@ -40,6 +42,7 @@ export interface ReportMakingComponentProps {
40
42
  type: ReportType;
41
43
  related?: PolymorphicRelation[]; // polymorphic relation to the parent entity
42
44
  revalidateCallback?: () => void;
45
+ biggerButton?: boolean; // if true, the button will be bigger
43
46
  }
44
47
 
45
48
  export interface SimpleReportFieldsProps {
@@ -0,0 +1,15 @@
1
+ import { CustomerProfileProps } from "./e-commerce/customer/types";
2
+ import { EnduserProfileProps } from "./e-commerce/enduser/types";
3
+ import { DispatcherProfileProps } from "./logistics/dispatcher/types";
4
+
5
+ export type User = {
6
+ id: string;
7
+ username: string;
8
+ email: string;
9
+ role?: "customer" | "enduser" | "dispatcher";
10
+ created_at: Date;
11
+ updated_at: Date;
12
+ customer_profile?: CustomerProfileProps;
13
+ enduser_profile?: EnduserProfileProps;
14
+ dispatcher_profile?: DispatcherProfileProps;
15
+ };