@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 +49 -49
- package/src/features/table/components/dashboard/table/details/details.tsx +7 -3
- package/src/features/table/components/dashboard/table/details/templates/invoice-details.tsx +87 -42
- package/src/features/table/components/dashboard/table/table-input.tsx +1 -1
- package/src/features/table/logic/resolvers.ts +2 -2
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
175
|
+
|
|
176
|
+
// Hide the # column for credit note types
|
|
145
177
|
const columns: TableColumn[] = [
|
|
146
|
-
|
|
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
|
-
<
|
|
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
|
-
{
|
|
303
|
+
{resolvedActions?.map((action) => {
|
|
269
304
|
const enabled = !action.isEnabled || action.isEnabled(row);
|
|
270
305
|
if (!enabled) {
|
|
271
306
|
return (
|
|
272
|
-
<
|
|
307
|
+
<Box
|
|
273
308
|
key={action.label}
|
|
274
|
-
|
|
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
|
-
{
|
|
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
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
|
600
|
-
{invoice?.Status !==
|
|
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
|
-
{
|
|
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(
|
|
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
|
</>
|
|
@@ -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
|
}
|