@evenicanpm/portal-table-ui 2.3.3 → 2.4.1

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.
package/package.json CHANGED
@@ -1,50 +1,50 @@
1
1
  {
2
- "name": "@evenicanpm/portal-table-ui",
3
- "version": "2.3.3",
4
- "description": "",
5
- "license": "ISC",
6
- "author": "",
7
- "type": "module",
8
- "scripts": {
9
- "test": "echo \"Error: no test specified\" && exit 1",
10
- "docs": "npx typedoc src --plugin typedoc-github-theme",
11
- "format": "npx @biomejs/biome format --write ./src",
12
- "build": "npx tsc"
13
- },
14
- "exports": {
15
- ".": "./src/index.ts"
16
- },
17
- "types": "./dist/index.d.ts",
18
- "files": [
19
- "dist",
20
- "src"
21
- ],
22
- "devDependencies": {
23
- "@emotion/react": "^11.11.3",
24
- "@emotion/styled": "^11.11.0",
25
- "@mui/icons-material": "^7.3.6",
26
- "@mui/material": "^6.1.7",
27
- "@types/react": "^18.3.24",
28
- "json-server": "^1.0.0-beta.3",
29
- "react": "^19.0.0",
30
- "typedoc": "^0.28.15",
31
- "typedoc-github-theme": "^0.3.1",
32
- "typescript": "^5.6.3"
33
- },
34
- "peerDependencies": {
35
- "@emotion/react": "^11.11.3",
36
- "@emotion/styled": "^11.11.0",
37
- "@mui/icons-material": "^7.3.6",
38
- "@mui/material": "^6.1.7",
39
- "@tanstack/react-query": "^5.90.11",
40
- "@types/react": "^18.3.24",
41
- "typescript": "^5.6.3"
42
- },
43
- "dependencies": {
44
- "@arrows/multimethod": "^2.1.0",
45
- "@evenicanpm/ui": "file:../../../common/@evenicanpm/ui",
46
- "@mui/lab": "^7.0.1-beta.19",
47
- "date-fns": "^4.1.0",
48
- "zod": "^4.4.3"
49
- }
50
- }
2
+ "name": "@evenicanpm/portal-table-ui",
3
+ "version": "2.4.1",
4
+ "description": "",
5
+ "license": "ISC",
6
+ "author": "",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": "./src/index.ts"
10
+ },
11
+ "types": "./dist/index.d.ts",
12
+ "files": [
13
+ "dist",
14
+ "src"
15
+ ],
16
+ "devDependencies": {
17
+ "@emotion/react": "^11.11.3",
18
+ "@emotion/styled": "^11.11.0",
19
+ "@mui/icons-material": "^6.1.7",
20
+ "@mui/material": "^6.1.7",
21
+ "@types/react": "^19.2.2",
22
+ "json-server": "^1.0.0-beta.3",
23
+ "react": "^19.2.7",
24
+ "typedoc": "^0.28.15",
25
+ "typedoc-github-theme": "^0.3.1",
26
+ "typescript": "^5.6.3"
27
+ },
28
+ "peerDependencies": {
29
+ "@emotion/react": "^11.11.3",
30
+ "@emotion/styled": "^11.11.0",
31
+ "@mui/icons-material": "^6.1.7",
32
+ "@mui/material": "^6.1.7",
33
+ "@tanstack/react-query": "^5.100.4",
34
+ "@types/react": "^19.2.2",
35
+ "typescript": "^5.6.3"
36
+ },
37
+ "dependencies": {
38
+ "@arrows/multimethod": "^2.1.0",
39
+ "@mui/lab": "^7.0.1-beta.19",
40
+ "date-fns": "^4.1.0",
41
+ "zod": "^4.4.3",
42
+ "@evenicanpm/ui": "2.4.1"
43
+ },
44
+ "scripts": {
45
+ "test": "echo \"Error: no test specified\" && exit 1",
46
+ "docs": "npx typedoc src --plugin typedoc-github-theme",
47
+ "format": "npx @biomejs/biome format --write ./src",
48
+ "build": "npx tsc"
49
+ }
50
+ }
@@ -124,7 +124,11 @@ const Details = ({
124
124
  resourceName={resourceName}
125
125
  />
126
126
  ) : (
127
- <Grid container spacing={2}>
127
+ <Grid
128
+ container
129
+ spacing={2}
130
+ sx={{ display: { xs: "block", sm: "flex" } }}
131
+ >
128
132
  {primitiveItems?.map((col) => {
129
133
  const maskedValue = col.maskFn(col.value, col.mask);
130
134
  const Component = getRowComponent(maskedValue, col.key);
@@ -199,8 +203,8 @@ const getRowComponent = multi(
199
203
  */
200
204
  const getRowLayout = multi(
201
205
  detailTypeDispatch,
202
- method("PRIMITIVE", () => ({ size: 6, display: "flex" })),
203
- method("STATUS", () => ({ size: 6, display: "flex" })),
206
+ method("PRIMITIVE", () => ({ size: { xs: 12, sm: 6 }, display: "flex" })),
207
+ method("STATUS", () => ({ size: { xs: 12, sm: 6 }, display: "flex" })),
204
208
  method("DOCUMENTS", () => ({ size: 12 })),
205
209
  method("OBJECT", () => ({ size: 12 })),
206
210
  );
@@ -4,6 +4,7 @@ import {
4
4
  Box,
5
5
  Button,
6
6
  Card,
7
+ Chip,
7
8
  Divider,
8
9
  Paper,
9
10
  Skeleton,
@@ -19,7 +20,7 @@ import {
19
20
  useTheme,
20
21
  } from "@mui/material";
21
22
  import Grid2 from "@mui/material/Grid2";
22
- import { StatusPill } from "../../status-pill";
23
+ import type { StatusColorMap } from "../../../../../types/schemas";
23
24
  import { ActionButton } from "../../table-row/action-button";
24
25
  import type { DetailsTemplateProps } from "./template-registry";
25
26
 
@@ -49,6 +50,19 @@ const formatDate = (dateString?: string): string => {
49
50
  }
50
51
  };
51
52
 
53
+ /**
54
+ * InvoiceType enum type.
55
+ */
56
+ export enum InvoiceType {
57
+ None = 0,
58
+ SalesOrderInvoice = 1,
59
+ FreeTextInvoice = 2,
60
+ ProjectInvoice = 3,
61
+ CreditNoteInvoice = 4,
62
+ FreeTextCreditNoteInvoice = 5,
63
+ ProjectCreditNoteInvoice = 6,
64
+ }
65
+
52
66
  interface InvoiceLine {
53
67
  ItemId?: string;
54
68
  ProductName?: string;
@@ -62,6 +76,7 @@ interface InvoiceLine {
62
76
 
63
77
  interface InvoiceData {
64
78
  Status?: string | number;
79
+ InvoiceType?: InvoiceType;
65
80
  IsOverdue?: boolean;
66
81
  SalesInvoiceLine?: InvoiceLine[];
67
82
  InvoiceSubtotal?: number;
@@ -82,6 +97,17 @@ interface InvoiceData {
82
97
  ChargeTotal?: number;
83
98
  }
84
99
 
100
+ export const statusColorMap: StatusColorMap = {
101
+ // Regular invoice labels
102
+ Unpaid: "primary",
103
+ "Partially Paid": "primary",
104
+ Paid: "success",
105
+ // Credit memo labels
106
+ Unused: "primary",
107
+ "Partially Used": "primary",
108
+ Used: "success",
109
+ };
110
+
85
111
  interface TableColumn {
86
112
  label: string;
87
113
  width: number;
@@ -103,9 +129,7 @@ const isDataEmpty = (data: InvoiceData | null | undefined): boolean => {
103
129
 
104
130
  export const InvoiceDetailsTemplate = ({
105
131
  data,
106
- statusColorMap,
107
132
  row,
108
- statusTextMap,
109
133
  loading = false,
110
134
  actions,
111
135
  resourceName = "invoice",
@@ -115,6 +139,13 @@ export const InvoiceDetailsTemplate = ({
115
139
 
116
140
  const invoice = data as InvoiceData;
117
141
 
142
+ const isCreditNote =
143
+ invoice?.InvoiceType === InvoiceType.CreditNoteInvoice ||
144
+ invoice?.InvoiceType === InvoiceType.FreeTextCreditNoteInvoice;
145
+
146
+ const isFreeTextInvoice =
147
+ invoice?.InvoiceType === InvoiceType.FreeTextInvoice;
148
+
118
149
  // Responsive width
119
150
  const containerWidth = isMobile ? "95%" : "75%";
120
151
 
@@ -141,9 +172,12 @@ export const InvoiceDetailsTemplate = ({
141
172
  const tax = invoice?.TotalTaxAmount ?? 0;
142
173
  const total = invoice?.Amount ?? 0;
143
174
  const chargeTotal = invoice?.ChargeTotal ?? 0;
144
- // Fixed column definitions with specific widths
175
+
176
+ // Hide the # column for credit note types
145
177
  const columns: TableColumn[] = [
146
- { label: "#", width: 60, aligned: "left" },
178
+ ...(!isCreditNote && !isFreeTextInvoice
179
+ ? [{ label: "#", width: 60, aligned: "left" as const }]
180
+ : []),
147
181
  { label: "Product", width: 200, aligned: "left" },
148
182
  { label: "Qty", width: 40, aligned: "right" },
149
183
  { label: "Unit", width: 60, aligned: "right" },
@@ -156,6 +190,11 @@ export const InvoiceDetailsTemplate = ({
156
190
  // Calculate total width for table container
157
191
  const totalTableWidth = columns.reduce((sum, col) => sum + col.width, 0);
158
192
 
193
+ // Remap action labels for credit notes
194
+ const resolvedActions = isCreditNote
195
+ ? actions?.map((a) => (a.label === "Pay" ? { ...a, label: "Use" } : a))
196
+ : actions;
197
+
159
198
  if (loading) {
160
199
  return (
161
200
  <Box sx={{ p: { xs: 2, sm: 3 }, width: containerWidth, mx: "auto" }}>
@@ -253,11 +292,7 @@ export const InvoiceDetailsTemplate = ({
253
292
  mb={3}
254
293
  >
255
294
  {invoice?.Status !== undefined && (
256
- <StatusPill
257
- status={invoice.Status}
258
- statusColorMap={statusColorMap}
259
- statusTextMap={statusTextMap}
260
- />
295
+ <Chip label={invoice.Status} color={statusColorMap[invoice.Status]} />
261
296
  )}
262
297
  <Box
263
298
  display="flex"
@@ -265,13 +300,13 @@ export const InvoiceDetailsTemplate = ({
265
300
  alignItems="center"
266
301
  sx={{ "@media print": { display: "none" } }}
267
302
  >
268
- {actions?.map((action) => {
303
+ {resolvedActions?.map((action) => {
269
304
  const enabled = !action.isEnabled || action.isEnabled(row);
270
305
  if (!enabled) {
271
306
  return (
272
- <span
307
+ <Box
273
308
  key={action.label}
274
- style={{
309
+ sx={{
275
310
  display: "inline-block",
276
311
  width: action.icon ? "34px" : "42px",
277
312
  height: "34px",
@@ -281,7 +316,6 @@ export const InvoiceDetailsTemplate = ({
281
316
  }
282
317
  return (
283
318
  <ActionButton
284
- key={action.label}
285
319
  docId={invoice.Id}
286
320
  action={action}
287
321
  resourceName={resourceName}
@@ -294,7 +328,7 @@ export const InvoiceDetailsTemplate = ({
294
328
  size="small"
295
329
  sx={{ "@media print": { display: "none" } }}
296
330
  >
297
- Print Invoice
331
+ {isCreditNote ? "Print Credit Memo" : "Print Invoice"}
298
332
  </Button>
299
333
  </Box>
300
334
  </Box>
@@ -414,20 +448,19 @@ export const InvoiceDetailsTemplate = ({
414
448
  {invoice?.InvoiceDate && (
415
449
  <Grid2 size={{ xs: 12, sm: 6 }}>
416
450
  <Typography variant="body2" color="text.secondary">
417
- Invoice Date
451
+ {isCreditNote ? "Date Issued" : "Invoice Date"}
418
452
  </Typography>
419
453
  <Typography>{formatDate(invoice.InvoiceDate)}</Typography>
420
454
  </Grid2>
421
455
  )}
422
- {invoice?.DueDate && (
456
+ {/* Due date hidden for credit notes */}
457
+ {invoice?.DueDate && !isCreditNote && (
423
458
  <Grid2 size={{ xs: 12, sm: 6 }}>
424
459
  <Typography variant="body2" color="text.secondary">
425
460
  Due Date
426
461
  </Typography>
427
-
428
462
  <Box sx={{ display: "flex", alignItems: "center", gap: 1 }}>
429
463
  <Typography>{formatDate(invoice.DueDate)}</Typography>
430
-
431
464
  {invoice?.IsOverdue && (
432
465
  <FlagIcon sx={{ color: "error.main" }} fontSize="small" />
433
466
  )}
@@ -480,17 +513,20 @@ export const InvoiceDetailsTemplate = ({
480
513
 
481
514
  return (
482
515
  <TableRow key={itemKey}>
483
- <TableCell
484
- sx={{
485
- width: "100%",
486
- textAlign: "left",
487
- "@media print": { width: 60, px: 1 },
488
- }}
489
- >
490
- <Typography variant="body2">
491
- {item?.ItemId || idx + 1}
492
- </Typography>
493
- </TableCell>
516
+ {/* Hide # column for credit note types */}
517
+ {!isCreditNote && !isFreeTextInvoice && (
518
+ <TableCell
519
+ sx={{
520
+ width: "100%",
521
+ textAlign: "left",
522
+ "@media print": { width: 60, px: 1 },
523
+ }}
524
+ >
525
+ <Typography variant="body2">
526
+ {item?.ItemId || idx + 1}
527
+ </Typography>
528
+ </TableCell>
529
+ )}
494
530
  <TableCell sx={{ width: "100%", textAlign: "left" }}>
495
531
  <Typography variant="body2">{productName}</Typography>
496
532
  </TableCell>
@@ -589,31 +625,40 @@ export const InvoiceDetailsTemplate = ({
589
625
  <Divider sx={{ my: 1.5 }} />
590
626
  <Box display="flex" justifyContent="space-between" mt={1}>
591
627
  <Typography variant="body1" fontWeight={700}>
592
- Total
628
+ {isCreditNote ? "Credit Total" : "Total"}
593
629
  </Typography>
594
630
  <Typography variant="body1" fontWeight={700}>
595
631
  {formatCurrency(total)}
596
632
  </Typography>
597
633
  </Box>
598
634
 
599
- {/* Paid and Remaining lines */}
600
- {invoice?.Status !== 1 && (
635
+ {/* Paid / Applied line */}
636
+ {invoice?.Status !== "Paid" && invoice?.Status !== "Used" && (
601
637
  <Box display="flex" justifyContent="space-between" mt={1}>
602
- <Typography variant="body2">Paid</Typography>
603
638
  <Typography variant="body2">
604
- {formatCurrency(total - (invoice?.AmountBalance ?? 0))}
639
+ {isCreditNote ? "Used" : "Paid"}
605
640
  </Typography>
606
- </Box>
607
- )}
608
-
609
- {invoice?.Status !== 1 && (invoice?.AmountBalance ?? 0) > 0 && (
610
- <Box display="flex" justifyContent="space-between" mt={0.5}>
611
- <Typography variant="body2">Remaining</Typography>
612
641
  <Typography variant="body2">
613
- {formatCurrency(invoice?.AmountBalance ?? 0)}
642
+ {formatCurrency(
643
+ Math.abs(total - (invoice?.AmountBalance ?? 0)),
644
+ )}
614
645
  </Typography>
615
646
  </Box>
616
647
  )}
648
+
649
+ {/* Remaining line */}
650
+ {invoice?.Status !== "Paid" &&
651
+ invoice?.Status !== "Used" &&
652
+ (invoice?.AmountBalance ?? 0) !== 0 && (
653
+ <Box display="flex" justifyContent="space-between" mt={0.5}>
654
+ <Typography variant="body2">
655
+ {isCreditNote ? "Remaining Credit" : "Remaining"}
656
+ </Typography>
657
+ <Typography variant="body2">
658
+ {formatCurrency(Math.abs(invoice?.AmountBalance ?? 0))}
659
+ </Typography>
660
+ </Box>
661
+ )}
617
662
  </Box>
618
663
  </Box>
619
664
  </>
@@ -25,7 +25,7 @@ const ColumnSelect = ({
25
25
  id="demo-simple-select"
26
26
  value={selected}
27
27
  sx={{
28
- maxWidth: "150px",
28
+ maxWidth: "180px",
29
29
  "& .MuiOutlinedInput-notchedOutline": { border: "none" },
30
30
  "& .MuiSelect-select": {
31
31
  paddingTop: 0,
@@ -85,7 +85,7 @@ export function resolveActions(
85
85
  return [
86
86
  {
87
87
  ...action,
88
- label: a.Name,
88
+ label: action.label ?? a.Name,
89
89
  },
90
90
  ];
91
91
  });
@@ -105,6 +105,6 @@ export function resolveBulkActions(
105
105
  console.warn(`Unknown bulk action type: ${a.Type}`);
106
106
  return [];
107
107
  }
108
- return [{ ...action, label: a.Name }];
108
+ return [{ ...action, label: action.label ?? a.Name }];
109
109
  });
110
110
  }