|
1 | | -"use client"; |
2 | | - |
3 | 1 | import { |
4 | 2 | Dialog, |
5 | 3 | DialogContent, |
6 | 4 | DialogTitle, |
7 | 5 | IconButton, |
8 | | - Table, |
9 | | - TableBody, |
10 | | - TableCell, |
11 | | - TableHead, |
12 | | - TableRow, |
13 | | - TableContainer, |
| 6 | + Stepper, |
| 7 | + Step, |
| 8 | + StepLabel, |
| 9 | + StepContent, |
| 10 | + Typography, |
14 | 11 | useTheme, |
15 | 12 | } from "@mui/material"; |
16 | 13 | import { Close } from "@mui/icons-material"; |
17 | | -import React, { useMemo } from "react"; |
18 | | -import { |
19 | | - useReactTable, |
20 | | - createColumnHelper, |
21 | | - getCoreRowModel, |
22 | | - flexRender, |
23 | | -} from "@tanstack/react-table"; |
24 | 14 | import { JobHistory } from "../../types/JobHistory"; |
25 | 15 |
|
26 | 16 | interface JobHistoryDialogProps { |
27 | | - /** Whether the Dialog is open */ |
| 17 | + /** Whether the dialog is open or not */ |
28 | 18 | open: boolean; |
29 | | - /** The function to close the dialog */ |
| 19 | + /** Function to close the dialog */ |
30 | 20 | onClose: () => void; |
31 | | - /** The data for the job history dialog */ |
| 21 | + /** Job history data */ |
32 | 22 | historyData: JobHistory[]; |
33 | | - /** The job ID */ |
| 23 | + /** Job ID */ |
34 | 24 | jobId: number; |
| 25 | + /** Status colors */ |
| 26 | + statusColors: Record<string, string>; |
| 27 | +} |
| 28 | + |
| 29 | +// Helper to group consecutive entries by Status |
| 30 | +function groupByConsecutiveStatus(history: JobHistory[]) { |
| 31 | + const groups: { status: string; entries: JobHistory[] }[] = []; |
| 32 | + let lastStatus: string | null = null; |
| 33 | + let currentGroup: JobHistory[] = []; |
| 34 | + for (const entry of history) { |
| 35 | + if (entry.Status !== lastStatus) { |
| 36 | + if (currentGroup.length > 0) { |
| 37 | + groups.push({ status: lastStatus!, entries: currentGroup }); |
| 38 | + } |
| 39 | + lastStatus = entry.Status; |
| 40 | + currentGroup = [entry]; |
| 41 | + } else { |
| 42 | + currentGroup.push(entry); |
| 43 | + } |
| 44 | + } |
| 45 | + if (currentGroup.length > 0) { |
| 46 | + groups.push({ status: lastStatus!, entries: currentGroup }); |
| 47 | + } |
| 48 | + return groups; |
35 | 49 | } |
36 | 50 |
|
37 | | -/** |
38 | | - * Renders a dialog component that displays the job history. |
39 | | - * |
40 | | - * @returns The rendered JobHistoryDialog component. |
41 | | - */ |
42 | 51 | export function JobHistoryDialog({ |
43 | 52 | open, |
44 | 53 | onClose, |
45 | 54 | historyData, |
46 | 55 | jobId, |
| 56 | + statusColors, |
47 | 57 | }: JobHistoryDialogProps) { |
48 | 58 | const theme = useTheme(); |
49 | 59 |
|
50 | | - // Create column helper |
51 | | - const columnHelper = createColumnHelper<JobHistory>(); |
| 60 | + // Reverse the history so the most recent is first |
| 61 | + const reversedHistory = [...historyData].reverse(); |
52 | 62 |
|
53 | | - // Define columns |
54 | | - const columns = useMemo( |
55 | | - () => [ |
56 | | - columnHelper.accessor("Status", { |
57 | | - header: "Status", |
58 | | - }), |
59 | | - columnHelper.accessor("MinorStatus", { |
60 | | - header: "Minor Status", |
61 | | - }), |
62 | | - columnHelper.accessor("ApplicationStatus", { |
63 | | - header: "Application Status", |
64 | | - }), |
65 | | - columnHelper.accessor("StatusTime", { |
66 | | - header: "Status Time", |
67 | | - }), |
68 | | - columnHelper.accessor("Source", { |
69 | | - header: "Source", |
70 | | - }), |
71 | | - ], |
72 | | - [columnHelper], |
73 | | - ); |
| 63 | + // Group consecutive entries by Status |
| 64 | + const grouped = groupByConsecutiveStatus(reversedHistory); |
74 | 65 |
|
75 | | - // Create table instance |
76 | | - const table = useReactTable({ |
77 | | - data: historyData, |
78 | | - columns, |
79 | | - getCoreRowModel: getCoreRowModel(), |
80 | | - state: {}, |
81 | | - enableColumnResizing: true, // Enable column resizing |
82 | | - columnResizeMode: "onChange", // Column resize mode |
83 | | - }); |
| 66 | + // Custom StepIcon for color logic |
| 67 | + const CustomStepIcon = (props: { |
| 68 | + active?: boolean; |
| 69 | + completed?: boolean; |
| 70 | + className?: string; |
| 71 | + }) => { |
| 72 | + const { active, completed, className } = props; |
| 73 | + let color = theme.palette.grey[400]; |
| 74 | + if (active) { |
| 75 | + color = statusColors[grouped[0].status] || theme.palette.primary.main; |
| 76 | + } else if (completed) { |
| 77 | + color = theme.palette.grey[400]; |
| 78 | + } |
| 79 | + return ( |
| 80 | + <span |
| 81 | + className={className} |
| 82 | + style={{ |
| 83 | + display: "flex", |
| 84 | + alignItems: "center", |
| 85 | + justifyContent: "center", |
| 86 | + width: 24, |
| 87 | + height: 24, |
| 88 | + borderRadius: "50%", |
| 89 | + background: color, |
| 90 | + }} |
| 91 | + /> |
| 92 | + ); |
| 93 | + }; |
84 | 94 |
|
85 | 95 | return ( |
86 | 96 | <Dialog open={open} onClose={onClose} aria-labelledby="job-history-title"> |
87 | 97 | <DialogTitle id="job-history-title">Job History: {jobId}</DialogTitle> |
88 | | - |
89 | 98 | <IconButton |
90 | 99 | aria-label="close" |
91 | 100 | onClick={onClose} |
92 | 101 | sx={{ |
93 | 102 | position: "absolute", |
94 | 103 | right: 8, |
95 | 104 | top: 8, |
96 | | - color: theme.palette.grey[500], |
97 | 105 | }} |
98 | 106 | > |
99 | 107 | <Close /> |
100 | 108 | </IconButton> |
| 109 | + <DialogContent sx={{ padding: 2 }}> |
| 110 | + <Stepper orientation="vertical" nonLinear activeStep={0}> |
| 111 | + {grouped.map((group, idx) => { |
| 112 | + const isActive = idx === 0; |
101 | 113 |
|
102 | | - <DialogContent sx={{ padding: 0 }}> |
103 | | - <TableContainer> |
104 | | - <Table stickyHeader> |
105 | | - <TableHead> |
106 | | - {table.getHeaderGroups().map((headerGroup) => ( |
107 | | - <TableRow key={headerGroup.id}> |
108 | | - {headerGroup.headers.map((header) => ( |
109 | | - <TableCell |
110 | | - key={header.id} |
111 | | - sx={{ |
112 | | - position: "relative", |
113 | | - width: header.getSize(), |
114 | | - }} |
115 | | - > |
116 | | - {header.isPlaceholder ? null : ( |
117 | | - <> |
118 | | - {flexRender( |
119 | | - header.column.columnDef.header, |
120 | | - header.getContext(), |
121 | | - )} |
122 | | - {header.column.getCanResize() && ( |
123 | | - <div |
124 | | - onMouseDown={header.getResizeHandler()} |
125 | | - onTouchStart={header.getResizeHandler()} |
126 | | - style={{ |
127 | | - position: "absolute", |
128 | | - right: 0, |
129 | | - top: 0, |
130 | | - height: "100%", |
131 | | - width: "5px", |
132 | | - cursor: "col-resize", |
133 | | - zIndex: 1, |
134 | | - }} |
135 | | - /> |
136 | | - )} |
137 | | - </> |
138 | | - )} |
139 | | - </TableCell> |
140 | | - ))} |
141 | | - </TableRow> |
142 | | - ))} |
143 | | - </TableHead> |
144 | | - <TableBody> |
145 | | - {table.getRowModel().rows.map((row) => ( |
146 | | - <TableRow key={row.id}> |
147 | | - {row.getVisibleCells().map((cell) => ( |
148 | | - <TableCell key={cell.id}> |
149 | | - {flexRender( |
150 | | - cell.column.columnDef.cell, |
151 | | - cell.getContext(), |
152 | | - )} |
153 | | - </TableCell> |
| 114 | + let labelColor: string = theme.palette.grey[400]; |
| 115 | + if (isActive) { |
| 116 | + labelColor = |
| 117 | + statusColors[group.status] || theme.palette.primary.main; |
| 118 | + } |
| 119 | + |
| 120 | + return ( |
| 121 | + <Step key={idx} expanded completed={!isActive}> |
| 122 | + <StepLabel slots={{ stepIcon: CustomStepIcon }}> |
| 123 | + <Typography fontWeight="bold" color={labelColor}> |
| 124 | + {group.status} |
| 125 | + </Typography> |
| 126 | + </StepLabel> |
| 127 | + <StepContent> |
| 128 | + {group.entries.map((entry, i) => ( |
| 129 | + <div key={i} style={{ marginBottom: 12 }}> |
| 130 | + <Typography variant="body2" fontWeight="bold"> |
| 131 | + {entry.MinorStatus} |
| 132 | + </Typography> |
| 133 | + <Typography variant="body2" sx={{ fontStyle: "italic" }}> |
| 134 | + {entry.ApplicationStatus} |
| 135 | + </Typography> |
| 136 | + <Typography |
| 137 | + variant="caption" |
| 138 | + display="block" |
| 139 | + sx={{ mt: 1 }} |
| 140 | + > |
| 141 | + {new Date(entry.StatusTime).toLocaleString("en-GB", { |
| 142 | + timeZone: "UTC", |
| 143 | + })}{" "} |
| 144 | + UTC |
| 145 | + <br /> |
| 146 | + {entry.Source} |
| 147 | + </Typography> |
| 148 | + </div> |
154 | 149 | ))} |
155 | | - </TableRow> |
156 | | - ))} |
157 | | - </TableBody> |
158 | | - </Table> |
159 | | - </TableContainer> |
| 150 | + </StepContent> |
| 151 | + </Step> |
| 152 | + ); |
| 153 | + })} |
| 154 | + </Stepper> |
160 | 155 | </DialogContent> |
161 | 156 | </Dialog> |
162 | 157 | ); |
|
0 commit comments