@evenicanpm/portal-table-ui 1.7.0 → 1.8.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,6 +1,6 @@
1
1
  {
2
2
  "name": "@evenicanpm/portal-table-ui",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "",
5
5
  "license": "ISC",
6
6
  "author": "",
@@ -41,11 +41,11 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@arrows/multimethod": "^2.1.0",
44
- "@evenicanpm/portal-types-schemas": "^1.7.0",
45
- "@evenicanpm/ui": "^1.6.0",
44
+ "@evenicanpm/portal-types-schemas": "^1.8.1",
45
+ "@evenicanpm/ui": "^1.8.1",
46
46
  "@mui/lab": "^7.0.1-beta.19",
47
47
  "@tanstack/react-query": "^5.90.11",
48
48
  "date-fns": "^4.1.0"
49
49
  },
50
- "gitHead": "2e25d32705901a4ff2dd43aa420fde931568ad93"
50
+ "gitHead": "07cfd4496c6a137e1420a6572298998b25b6369d"
51
51
  }
@@ -1,14 +1,13 @@
1
1
  import { Badge, Button, CircularProgress, IconButton } from "@mui/material";
2
2
  import { useMutation } from "@tanstack/react-query";
3
- import type { Row } from "src/features/table/types/queries";
4
- import type { UIBulkAction } from "src/features/table/types/schemas";
3
+ import type * as Types from "../../../types";
5
4
 
6
5
  interface BulkActionButtonProps {
7
- action: UIBulkAction;
6
+ action: Types.Schemas.UIBulkAction;
8
7
  resourceName: string;
9
8
  selectedIds: (string | number)[];
10
9
  clearSelection: () => void;
11
- selectedRows: Row[];
10
+ selectedRows: Types.Queries.Row[];
12
11
  }
13
12
  export function BulkActionButton({
14
13
  action,
@@ -34,6 +34,9 @@ interface DetailsProps {
34
34
  statusTextMap: StatusTextMap;
35
35
  template?: string;
36
36
  loading?: boolean;
37
+
38
+ actions?: Types.Schemas.UIAction[];
39
+ resourceName?: string;
37
40
  }
38
41
 
39
42
  /**
@@ -49,6 +52,8 @@ const Details = ({
49
52
  statusColorMap,
50
53
  statusTextMap,
51
54
  template,
55
+ actions,
56
+ resourceName,
52
57
  loading = false,
53
58
  }: DetailsProps) => {
54
59
  // Resolve the template component once — undefined when the key is absent or
@@ -96,6 +101,8 @@ const Details = ({
96
101
  statusColorMap={statusColorMap}
97
102
  statusTextMap={statusTextMap}
98
103
  loading={loading}
104
+ actions={actions}
105
+ resourceName={resourceName}
99
106
  />
100
107
  ) : (
101
108
  <Grid container spacing={2}>
@@ -1,14 +1,23 @@
1
1
  "use client";
2
2
  import {
3
3
  Box,
4
- Button,
5
4
  Card,
6
5
  Divider,
6
+ Paper,
7
7
  Skeleton,
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableContainer,
12
+ TableHead,
13
+ TableRow,
8
14
  Typography,
15
+ useMediaQuery,
16
+ useTheme,
9
17
  } from "@mui/material";
10
18
  import Grid2 from "@mui/material/Grid2";
11
19
  import { StatusPill } from "../../status-pill";
20
+ import { ActionButton } from "../../table-row/action-button";
12
21
  import type { DetailsTemplateProps } from "./template-registry";
13
22
 
14
23
  // Formatting utilities
@@ -24,12 +33,9 @@ const formatCurrency = (amount?: number): string => {
24
33
 
25
34
  const formatDate = (dateString?: string): string => {
26
35
  if (!dateString) return "—";
27
-
28
36
  try {
29
37
  const date = new Date(dateString);
30
- // Check if date is valid
31
38
  if (isNaN(date.getTime())) return dateString;
32
-
33
39
  return new Intl.DateTimeFormat("en-US", {
34
40
  year: "numeric",
35
41
  month: "long",
@@ -41,15 +47,15 @@ const formatDate = (dateString?: string): string => {
41
47
  };
42
48
 
43
49
  interface InvoiceLine {
44
- Description?: string;
45
50
  ItemId?: string;
51
+ ProductName?: string;
46
52
  Quantity?: number;
47
53
  Price?: number;
48
- LineAmount?: number;
54
+ NetAmount?: number;
49
55
  UnitOfMeasure?: string;
50
- FullDescription?: string;
51
- Savings?: number;
52
- IsOnSale?: boolean;
56
+ LineDiscount?: number;
57
+ DiscountAmount: number;
58
+ HasDiscount: boolean;
53
59
  }
54
60
 
55
61
  interface InvoiceData {
@@ -67,16 +73,15 @@ interface InvoiceData {
67
73
  InvoiceDate?: string;
68
74
  DueDate?: string;
69
75
  Notes?: string;
76
+ Id: string;
70
77
  }
71
78
 
72
79
  const isDataEmpty = (data: InvoiceData | null | undefined): boolean => {
73
80
  if (!data) return true;
74
-
75
81
  const hasNoLineItems =
76
82
  !data.SalesInvoiceLine || data.SalesInvoiceLine.length === 0;
77
83
  const hasNoBasicInfo = !data.CompanyName && !data.Name && !data.InvoiceDate;
78
84
  const hasNoAmounts = !data.Amount && data.Amount !== 0;
79
-
80
85
  return hasNoLineItems && hasNoBasicInfo && hasNoAmounts;
81
86
  };
82
87
 
@@ -85,18 +90,28 @@ export const InvoiceDetailsTemplate = ({
85
90
  statusColorMap,
86
91
  statusTextMap,
87
92
  loading = false,
93
+ actions,
94
+ resourceName = "invoice",
88
95
  }: DetailsTemplateProps) => {
96
+ const theme = useTheme();
97
+ const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
98
+
89
99
  const invoice = data as InvoiceData;
90
100
 
101
+ // Responsive width
102
+ const containerWidth = isMobile ? "95%" : "75%";
103
+
91
104
  if (!loading && (!invoice || isDataEmpty(invoice))) {
92
105
  return (
93
- <Box sx={{ p: 3, width: "75%", mx: "auto" }}>
94
- <Card sx={{ p: 6, borderRadius: 3, textAlign: "center" }}>
106
+ <Box sx={{ p: { xs: 2, sm: 3 }, width: containerWidth, mx: "auto" }}>
107
+ <Card
108
+ sx={{ p: { xs: 3, sm: 6 }, borderRadius: 3, textAlign: "center" }}
109
+ >
95
110
  <Typography variant="h6" color="text.secondary" gutterBottom>
96
111
  No Data Available
97
112
  </Typography>
98
113
  <Typography variant="body2" color="text.secondary">
99
- This invoice template has no data to display.
114
+ We couldn't find any details for selected invoice.
100
115
  </Typography>
101
116
  </Card>
102
117
  </Box>
@@ -108,56 +123,80 @@ export const InvoiceDetailsTemplate = ({
108
123
  const tax = invoice?.TotalTaxAmount ?? 0;
109
124
  const total = invoice?.Amount ?? 0;
110
125
 
126
+ // Fixed column definitions with specific widths
111
127
  const columns = [
112
- { label: "#", size: 1 },
113
- { label: "Product", size: 3 },
114
- { label: "Qty", size: 1 },
115
- { label: "Unit", size: 2 },
116
- { label: "Unit Price", size: 1.5 },
117
- { label: "Savings", size: 1.5 },
118
- { label: "Line Total", size: 1.5 },
119
- { label: "", size: 0.5 },
128
+ { label: "#", width: 60 },
129
+ { label: "Product", width: 200 },
130
+ { label: "Qty", width: 60 },
131
+ { label: "Unit", width: 60 },
132
+ { label: "Unit Price", width: 120 },
133
+ { label: "Discount", width: 100 },
134
+ { label: "Line Total", width: 120 },
135
+ { label: "", width: 40 },
120
136
  ];
121
137
 
138
+ // Calculate total width for table container
139
+ const totalTableWidth = columns.reduce((sum, col) => sum + col.width, 0);
140
+
122
141
  if (loading) {
123
142
  return (
124
- <Box sx={{ p: 3, width: "75%", mx: "auto" }}>
143
+ <Box sx={{ p: { xs: 2, sm: 3 }, width: containerWidth, mx: "auto" }}>
125
144
  <Box display="flex" justifyContent="space-between" mb={3}>
126
145
  <Skeleton variant="rectangular" width={100} height={32} />
127
146
  <Skeleton variant="rectangular" width={120} height={40} />
128
147
  </Box>
129
- <Card sx={{ p: 4, borderRadius: 3 }}>
130
- <Grid2 container spacing={4}>
148
+ <Card sx={{ p: { xs: 2, sm: 4 }, borderRadius: 3 }}>
149
+ <Grid2 container spacing={{ xs: 2, sm: 4 }}>
131
150
  {Array.from({ length: 4 }).map((_, i) => (
132
- <Grid2 size={6} key={i}>
151
+ <Grid2 size={{ xs: 12, sm: 6 }} key={`skeleton-${i}`}>
133
152
  <Skeleton width="40%" height={20} />
134
153
  <Skeleton width="80%" />
135
154
  <Skeleton width="60%" />
136
155
  </Grid2>
137
156
  ))}
138
157
  </Grid2>
139
- <Divider sx={{ my: 3 }} />
140
- <Grid2 container sx={{ mb: 2 }}>
141
- {columns.map((col, i) => (
142
- <Grid2 key={i} size={col.size}>
143
- <Skeleton width="60%" />
144
- </Grid2>
145
- ))}
146
- </Grid2>
147
- {Array.from({ length: 4 }).map((_, idx) => (
148
- <Grid2 container key={idx} sx={{ py: 1.5 }}>
149
- {columns.map((col, i) => (
150
- <Grid2 key={i} size={col.size}>
151
- <Skeleton width="80%" />
152
- </Grid2>
153
- ))}
154
- </Grid2>
155
- ))}
158
+ <Divider sx={{ my: { xs: 2, sm: 3 } }} />
159
+
160
+ {/* Table skeleton with scroll */}
161
+ <TableContainer
162
+ component={Paper}
163
+ sx={{ boxShadow: "none", overflowX: "auto" }}
164
+ >
165
+ <Table sx={{ minWidth: totalTableWidth }}>
166
+ <TableHead>
167
+ <TableRow>
168
+ {columns.map((col) => (
169
+ <TableCell
170
+ key={col.label}
171
+ sx={{ width: col.width, minWidth: col.width }}
172
+ >
173
+ <Skeleton width="80%" />
174
+ </TableCell>
175
+ ))}
176
+ </TableRow>
177
+ </TableHead>
178
+ <TableBody>
179
+ {Array.from({ length: 4 }).map((_, idx) => (
180
+ <TableRow key={`skeleton-${idx}`}>
181
+ {columns.map((col) => (
182
+ <TableCell
183
+ key={col.label}
184
+ sx={{ width: col.width, minWidth: col.width }}
185
+ >
186
+ <Skeleton width="80%" />
187
+ </TableCell>
188
+ ))}
189
+ </TableRow>
190
+ ))}
191
+ </TableBody>
192
+ </Table>
193
+ </TableContainer>
194
+
156
195
  <Box mt={3} display="flex" justifyContent="flex-end">
157
- <Box width={300}>
196
+ <Box width={{ xs: "100%", sm: 300 }}>
158
197
  {Array.from({ length: 3 }).map((_, i) => (
159
198
  <Box
160
- key={i}
199
+ key={`skeleton-${i}`}
161
200
  display="flex"
162
201
  justifyContent="space-between"
163
202
  mb={1}
@@ -174,12 +213,14 @@ export const InvoiceDetailsTemplate = ({
174
213
  }
175
214
 
176
215
  return (
177
- <Box sx={{ p: 3, width: "75%", mx: "auto" }}>
216
+ <Box sx={{ p: { xs: 2, sm: 3 }, width: containerWidth, mx: "auto" }}>
178
217
  {/* Header */}
179
218
  <Box
180
219
  display="flex"
181
220
  justifyContent="space-between"
182
221
  alignItems="center"
222
+ flexWrap="wrap"
223
+ gap={2}
183
224
  mb={3}
184
225
  >
185
226
  {invoice?.Status !== undefined && (
@@ -189,16 +230,37 @@ export const InvoiceDetailsTemplate = ({
189
230
  statusTextMap={statusTextMap}
190
231
  />
191
232
  )}
192
- {invoice?.Status !== 3 && invoice?.Status !== undefined && (
193
- <Button variant="contained" color="warning">
194
- Pay Now
195
- </Button>
196
- )}
233
+ <Box display="flex" gap={2} alignItems="center">
234
+ {actions?.map((action) => {
235
+ const isPaid = invoice.Status === 3;
236
+ const enabled = !isPaid && action.isEnabled;
237
+ if (!enabled) {
238
+ return (
239
+ <span
240
+ key={action.label}
241
+ style={{
242
+ display: "inline-block",
243
+ width: action.icon ? "34px" : "42px",
244
+ height: "34px",
245
+ }}
246
+ />
247
+ );
248
+ }
249
+ return (
250
+ <ActionButton
251
+ key={action.label}
252
+ docId={invoice.Id}
253
+ action={action}
254
+ resourceName={resourceName}
255
+ />
256
+ );
257
+ })}
258
+ </Box>
197
259
  </Box>
198
260
 
199
261
  {/* Notes Section */}
200
262
  {invoice?.Notes && (
201
- <Card sx={{ p: 3, borderRadius: 3, mb: 3 }}>
263
+ <Card sx={{ p: { xs: 2, sm: 3 }, borderRadius: 3, mb: 3 }}>
202
264
  <Typography variant="subtitle1" color="text.secondary" mb={1}>
203
265
  Notes
204
266
  </Typography>
@@ -213,7 +275,7 @@ export const InvoiceDetailsTemplate = ({
213
275
  )}
214
276
 
215
277
  {/* Main Invoice Card */}
216
- <Card sx={{ p: 3, borderRadius: 3 }}>
278
+ <Card sx={{ p: { xs: 2, sm: 3 }, borderRadius: 3 }}>
217
279
  {/* Details Section */}
218
280
  {(invoice?.CompanyName ||
219
281
  invoice?.Name ||
@@ -223,10 +285,9 @@ export const InvoiceDetailsTemplate = ({
223
285
  <Typography variant="subtitle1" color="text.secondary" mb={2}>
224
286
  Details
225
287
  </Typography>
226
-
227
- <Grid2 container spacing={4}>
288
+ <Grid2 container spacing={{ xs: 2, sm: 4 }}>
228
289
  {invoice?.CompanyName && (
229
- <Grid2 size={6}>
290
+ <Grid2 size={{ xs: 12, sm: 6 }}>
230
291
  <Typography variant="body2" color="text.secondary">
231
292
  From
232
293
  </Typography>
@@ -234,46 +295,45 @@ export const InvoiceDetailsTemplate = ({
234
295
  {invoice.CompanyName}
235
296
  </Typography>
236
297
  {invoice.CompanyEmail && (
237
- <Typography variant="body2">
298
+ <Typography variant="body2" sx={{ wordBreak: "break-all" }}>
238
299
  {invoice.CompanyEmail}
239
300
  </Typography>
240
301
  )}
241
302
  {invoice.CompanyAddress && (
242
- <Typography variant="body2">
303
+ <Typography variant="body2" sx={{ whiteSpace: "pre-wrap" }}>
243
304
  {invoice.CompanyAddress}
244
305
  </Typography>
245
306
  )}
246
307
  </Grid2>
247
308
  )}
248
-
249
309
  {invoice?.Name && (
250
- <Grid2 size={6}>
310
+ <Grid2 size={{ xs: 12, sm: 6 }}>
251
311
  <Typography variant="body2" color="text.secondary">
252
312
  Bill To
253
313
  </Typography>
254
314
  <Typography fontWeight={600}>{invoice.Name}</Typography>
255
315
  {invoice.Email && (
256
- <Typography variant="body2">{invoice.Email}</Typography>
316
+ <Typography variant="body2" sx={{ wordBreak: "break-all" }}>
317
+ {invoice.Email}
318
+ </Typography>
257
319
  )}
258
320
  {invoice.BillingAddress && (
259
- <Typography variant="body2">
321
+ <Typography variant="body2" sx={{ whiteSpace: "pre-wrap" }}>
260
322
  {invoice.BillingAddress}
261
323
  </Typography>
262
324
  )}
263
325
  </Grid2>
264
326
  )}
265
-
266
327
  {invoice?.InvoiceDate && (
267
- <Grid2 size={6}>
328
+ <Grid2 size={{ xs: 12, sm: 6 }}>
268
329
  <Typography variant="body2" color="text.secondary">
269
330
  Invoice Date
270
331
  </Typography>
271
332
  <Typography>{formatDate(invoice.InvoiceDate)}</Typography>
272
333
  </Grid2>
273
334
  )}
274
-
275
335
  {invoice?.DueDate && (
276
- <Grid2 size={6}>
336
+ <Grid2 size={{ xs: 12, sm: 6 }}>
277
337
  <Typography variant="body2" color="text.secondary">
278
338
  Due Date
279
339
  </Typography>
@@ -281,108 +341,114 @@ export const InvoiceDetailsTemplate = ({
281
341
  </Grid2>
282
342
  )}
283
343
  </Grid2>
284
-
285
- <Divider sx={{ my: 3 }} />
344
+ <Divider sx={{ my: { xs: 2, sm: 3 } }} />
286
345
  </>
287
346
  )}
288
347
 
289
348
  {/* Line Items Section */}
290
349
  {items.length > 0 ? (
291
350
  <>
292
- {/* Table Header */}
293
- <Grid2 container sx={{ mb: 1 }}>
294
- {columns.map((col, i) => (
295
- <Grid2 key={i} size={col.size}>
296
- <Typography variant="body2" fontWeight={600}>
297
- {col.label}
298
- </Typography>
299
- </Grid2>
300
- ))}
301
- </Grid2>
302
-
303
- {/* Line Items */}
304
- {items.map((item, idx) => (
305
- <Grid2
306
- container
307
- key={idx}
308
- sx={{ py: 1.5, borderBottom: "1px solid #eee" }}
309
- >
310
- <Grid2 size={1}>
311
- <Typography variant="body2">{item?.ItemId || "—"}</Typography>
312
- </Grid2>
313
-
314
- <Grid2 size={3}>
315
- <Typography variant="body2">
316
- {item?.Description || "—"}
317
- </Typography>
318
- </Grid2>
319
-
320
- <Grid2 size={1}>
321
- <Typography variant="body2">
322
- {item?.Quantity ?? "—"}
323
- </Typography>
324
- </Grid2>
325
-
326
- <Grid2 size={1.5}>
327
- <Typography variant="body2">
328
- {item?.UnitOfMeasure || ""}
329
- </Typography>
330
- </Grid2>
331
-
332
- <Grid2 size={2}>
333
- <Typography variant="body2">
334
- {formatCurrency(item?.Price)}
335
- </Typography>
336
- </Grid2>
337
-
338
- <Grid2 size={1.5}>
339
- <Typography variant="body2">
340
- {item?.Savings
341
- ? formatCurrency(item.Savings)
342
- : formatCurrency(0)}
343
- </Typography>
344
- </Grid2>
345
-
346
- <Grid2 size={1.5}>
347
- <Typography variant="body2">
348
- {formatCurrency(item?.LineAmount)}
349
- </Typography>
350
- </Grid2>
351
-
352
- <Grid2
353
- size={0.5}
354
- display="flex"
355
- alignItems="center"
356
- justifyContent="center"
357
- >
358
- {item?.IsOnSale && (
359
- <Typography color="error.main" fontWeight={700}>
360
- *
361
- </Typography>
362
- )}
363
- </Grid2>
364
- </Grid2>
365
- ))}
351
+ <TableContainer>
352
+ <Table sx={{ minWidth: totalTableWidth }}>
353
+ <TableHead>
354
+ <TableRow>
355
+ {columns.map((col) => (
356
+ <TableCell
357
+ key={col.label}
358
+ sx={{
359
+ width: col.width,
360
+ minWidth: col.width,
361
+ fontWeight: 600,
362
+ backgroundColor: theme.palette.background.paper,
363
+ }}
364
+ >
365
+ <Typography variant="body2" fontWeight={600}>
366
+ {col.label}
367
+ </Typography>
368
+ </TableCell>
369
+ ))}
370
+ </TableRow>
371
+ </TableHead>
372
+ <TableBody>
373
+ {items.map((item, idx) => {
374
+ const productName = item.ProductName || "-";
375
+ const unitPrice = item.Price ?? 0;
376
+ const quantity = item.Quantity ?? 0;
377
+ const lineTotal = item.NetAmount ?? unitPrice * quantity;
378
+
379
+ return (
380
+ <TableRow key={`${item.ItemId}-${idx}`}>
381
+ <TableCell sx={{ width: 60, minWidth: 60 }}>
382
+ <Typography variant="body2">
383
+ {item?.ItemId || idx + 1}
384
+ </Typography>
385
+ </TableCell>
386
+ <TableCell sx={{ width: 200, minWidth: 200 }}>
387
+ <Typography variant="body2">{productName}</Typography>
388
+ </TableCell>
389
+ <TableCell sx={{ width: 60, minWidth: 60 }}>
390
+ <Typography variant="body2">{quantity}</Typography>
391
+ </TableCell>
392
+ <TableCell sx={{ width: 60, minWidth: 60 }}>
393
+ <Typography variant="body2">
394
+ {item?.UnitOfMeasure || "—"}
395
+ </Typography>
396
+ </TableCell>
397
+ <TableCell sx={{ width: 120, minWidth: 120 }}>
398
+ <Typography variant="body2">
399
+ {formatCurrency(unitPrice)}
400
+ </Typography>
401
+ </TableCell>
402
+ <TableCell sx={{ width: 100, minWidth: 100 }}>
403
+ <Typography
404
+ variant="body2"
405
+ color={
406
+ item.HasDiscount ? "error.main" : "text.secondary"
407
+ }
408
+ >
409
+ {item.HasDiscount
410
+ ? `-${formatCurrency(item.DiscountAmount)}`
411
+ : formatCurrency(0)}
412
+ </Typography>
413
+ </TableCell>
414
+ <TableCell sx={{ width: 100, minWidth: 100 }}>
415
+ <Typography variant="body2" fontWeight={500}>
416
+ {formatCurrency(lineTotal)}
417
+ </Typography>
418
+ </TableCell>
419
+ <TableCell
420
+ sx={{ width: 40, minWidth: 40 }}
421
+ align="center"
422
+ >
423
+ {item.HasDiscount && (
424
+ <Typography color="error.main" fontWeight={700}>
425
+ *
426
+ </Typography>
427
+ )}
428
+ </TableCell>
429
+ </TableRow>
430
+ );
431
+ })}
432
+ </TableBody>
433
+ </Table>
434
+ </TableContainer>
366
435
 
367
436
  {/* Totals */}
368
437
  <Box mt={3} display="flex" justifyContent="flex-end">
369
- <Box width={300}>
438
+ <Box width={{ xs: "100%", sm: 300 }}>
370
439
  <Box display="flex" justifyContent="space-between" mb={1}>
371
440
  <Typography variant="body2">Subtotal</Typography>
372
441
  <Typography variant="body2" fontWeight={500}>
373
442
  {formatCurrency(subtotal)}
374
443
  </Typography>
375
444
  </Box>
376
-
377
445
  <Box display="flex" justifyContent="space-between" mb={1}>
378
446
  <Typography variant="body2">Tax</Typography>
379
447
  <Typography variant="body2" fontWeight={500}>
380
448
  {formatCurrency(tax)}
381
449
  </Typography>
382
450
  </Box>
383
-
384
451
  <Divider sx={{ my: 1.5 }} />
385
-
386
452
  <Box display="flex" justifyContent="space-between" mt={1}>
387
453
  <Typography variant="body1" fontWeight={700}>
388
454
  Total
@@ -1,7 +1,4 @@
1
- import type {
2
- StatusColorMap,
3
- StatusTextMap,
4
- } from "src/features/table/types/schemas";
1
+ import type * as Types from "../../../../../types";
5
2
  import { InvoiceDetailsTemplate } from "./invoice-details";
6
3
 
7
4
  /**
@@ -11,9 +8,11 @@ import { InvoiceDetailsTemplate } from "./invoice-details";
11
8
  */
12
9
  export interface DetailsTemplateProps {
13
10
  data: unknown;
14
- statusColorMap: StatusColorMap;
15
- statusTextMap: StatusTextMap;
11
+ statusColorMap: Types.Schemas.StatusColorMap;
12
+ statusTextMap: Types.Schemas.StatusTextMap;
16
13
  loading?: boolean;
14
+ actions?: Types.Schemas.UIAction[];
15
+ resourceName?: string;
17
16
  }
18
17
 
19
18
  /**
@@ -1,4 +1,3 @@
1
- import { Action } from "@evenicanpm/portal-types-schemas";
2
1
  import { FlexBox } from "@evenicanpm/ui";
3
2
  import Checkbox from "@mui/material/Checkbox";
4
3
  import TableCell from "@mui/material/TableCell";
@@ -203,6 +203,24 @@ export default function DocumentTable({
203
203
  },
204
204
  );
205
205
 
206
+ const resolvedActions = resolvers.resolveActions(
207
+ config?.Actions,
208
+ actionRegistry,
209
+ );
210
+ const resolvedBulkActions = resolvers.resolveBulkActions(
211
+ config?.BulkActions,
212
+ bulkRegistry,
213
+ );
214
+
215
+ const hasActions =
216
+ configPending ||
217
+ (resolvedActions.length > 0 &&
218
+ rows?.data?.some((row) =>
219
+ resolvedActions.some((a) => !a.isEnabled || a.isEnabled(row)),
220
+ )) ||
221
+ false;
222
+ const hasBulkActions = resolvedBulkActions.length > 0;
223
+
206
224
  // --- Render --- //
207
225
  if (selectedDocument) {
208
226
  const rowSnapshot =
@@ -224,28 +242,12 @@ export default function DocumentTable({
224
242
  detailsRenderMap={detailsRenderMap}
225
243
  template={hasTemplateSupport ? resource.detailsTemplate : undefined}
226
244
  loading={hasTemplateSupport ? detailsPending : false}
245
+ actions={resolvedActions}
246
+ resourceName={resource.name}
227
247
  />
228
248
  );
229
249
  }
230
250
 
231
- const resolvedActions = resolvers.resolveActions(
232
- config?.Actions,
233
- actionRegistry,
234
- );
235
- const resolvedBulkActions = resolvers.resolveBulkActions(
236
- config?.BulkActions,
237
- bulkRegistry,
238
- );
239
-
240
- const hasActions =
241
- configPending ||
242
- (resolvedActions.length > 0 &&
243
- rows?.data?.some((row) =>
244
- resolvedActions.some((a) => !a.isEnabled || a.isEnabled(row)),
245
- )) ||
246
- false;
247
- const hasBulkActions = resolvedBulkActions.length > 0;
248
-
249
251
  const baseCols = (visibleCols?.length ?? 0) + 1;
250
252
  const colSpan = hasActions ? baseCols + 1 : baseCols;
251
253
  const noResults = !rowsPending && !configPending && rows?.data?.length === 0;