FPSMS-frontend
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 

203 wiersze
6.9 KiB

  1. import React, { useState } from 'react';
  2. import {
  3. Box,
  4. Paper,
  5. Table,
  6. TableBody,
  7. TableCell,
  8. TableContainer,
  9. TableHead,
  10. TableRow,
  11. Typography,
  12. Chip,
  13. Card,
  14. CardContent,
  15. } from '@mui/material';
  16. import {
  17. useReactTable,
  18. getCoreRowModel,
  19. createColumnHelper,
  20. flexRender,
  21. } from '@tanstack/react-table';
  22. import DescriptionIcon from '@mui/icons-material/Description';
  23. import CheckCircleIcon from '@mui/icons-material/CheckCircle';
  24. import CancelIcon from '@mui/icons-material/Cancel';
  25. import CalendarTodayIcon from '@mui/icons-material/CalendarToday';
  26. interface QCReport {
  27. id: string;
  28. productName: string;
  29. isQualified: boolean;
  30. remarks: string;
  31. reportDate: string;
  32. batchNumber: string;
  33. inspector: string;
  34. testResults: string;
  35. specifications: string;
  36. }
  37. interface QCReportFormProps {
  38. reports: QCReport[];
  39. }
  40. function QCReportForm({ reports }: QCReportFormProps): JSX.Element {
  41. const [selectedReport, setSelectedReport] = useState<QCReport | null>(null);
  42. const handleReportClick = (report: QCReport): void => {
  43. setSelectedReport(report);
  44. };
  45. const getQualifiedStatus = (isQualified: boolean) => {
  46. return isQualified ? (
  47. <Chip
  48. icon={<CheckCircleIcon />}
  49. label="合格"
  50. color="success"
  51. size="small"
  52. sx={{ fontWeight: 'medium' }}
  53. />
  54. ) : (
  55. <Chip
  56. icon={<CancelIcon />}
  57. label="不合格"
  58. color="error"
  59. size="small"
  60. sx={{ fontWeight: 'medium' }}
  61. />
  62. );
  63. };
  64. const columnHelper = createColumnHelper<QCReport>();
  65. const columns = [
  66. // columnHelper.accessor('productName', {
  67. // header: '產品',
  68. // cell: info => (
  69. // <Typography variant="body2" fontWeight="medium">
  70. // {info.getValue()}
  71. // </Typography>
  72. // ),
  73. // }),
  74. columnHelper.accessor('isQualified', {
  75. header: '合格',
  76. cell: info => getQualifiedStatus(info.getValue()),
  77. }),
  78. columnHelper.accessor('remarks', {
  79. header: '備註',
  80. cell: info => (
  81. <Typography
  82. variant="body2"
  83. color="text.secondary"
  84. sx={{ maxWidth: '200px', overflow: 'hidden', textOverflow: 'ellipsis' }}
  85. >
  86. {info.getValue()}
  87. </Typography>
  88. ),
  89. }),
  90. columnHelper.accessor('reportDate', {
  91. header: '上報日期',
  92. cell: info => (
  93. <Typography variant="body2" color="text.secondary">
  94. {info.getValue()}
  95. </Typography>
  96. ),
  97. }),
  98. ];
  99. const table = useReactTable({
  100. data: reports,
  101. columns,
  102. getCoreRowModel: getCoreRowModel(),
  103. });
  104. return (
  105. <Box sx={{ maxWidth: '1200px', mx: 'auto', p: 3 }}>
  106. {/* Table Section */}
  107. <Box>
  108. <Typography variant="h6" fontWeight="medium" sx={{ mb: 2 }}>
  109. 上報資料
  110. </Typography>
  111. <TableContainer component={Paper} elevation={2}>
  112. <Table>
  113. <TableHead sx={{ bgcolor: 'grey.50' }}>
  114. {table.getHeaderGroups().map(headerGroup => (
  115. <TableRow key={headerGroup.id}>
  116. {headerGroup.headers.map(header => (
  117. <TableCell
  118. key={header.id}
  119. sx={{
  120. fontSize: '0.75rem',
  121. fontWeight: 'medium',
  122. color: 'text.secondary',
  123. textTransform: 'uppercase',
  124. py: 2,
  125. px: 3,
  126. }}
  127. >
  128. {header.isPlaceholder
  129. ? null
  130. : flexRender(header.column.columnDef.header, header.getContext())}
  131. </TableCell>
  132. ))}
  133. </TableRow>
  134. ))}
  135. </TableHead>
  136. <TableBody>
  137. {table.getRowModel().rows.map(row => (
  138. <TableRow
  139. key={row.id}
  140. onClick={() => handleReportClick(row.original)}
  141. sx={{
  142. cursor: 'pointer',
  143. '&:hover': { bgcolor: 'grey.50' },
  144. bgcolor: selectedReport?.id === row.original.id ? 'primary.light' : 'inherit',
  145. transition: 'background-color 0.15s',
  146. }}
  147. >
  148. {row.getVisibleCells().map(cell => (
  149. <TableCell key={cell.id} sx={{ py: 2, px: 3 }}>
  150. {flexRender(cell.column.columnDef.cell, cell.getContext())}
  151. </TableCell>
  152. ))}
  153. </TableRow>
  154. ))}
  155. </TableBody>
  156. </Table>
  157. </TableContainer>
  158. </Box>
  159. </Box>
  160. );
  161. }
  162. // Dummy data
  163. const dummyReports = [
  164. {
  165. id: '1',
  166. productName: '無線藍牙耳機',
  167. isQualified: false,
  168. remarks: '包裝有破損',
  169. reportDate: '2024-08-06',
  170. batchNumber: 'WBE-240806-002',
  171. inspector: '李測試',
  172. testResults: '音質測試:左右聲道音量差異>3dB,不符合標準',
  173. specifications: '頻響範圍20Hz-20kHz,左右聲道音量差≤1dB,電池續航≥8小時',
  174. },
  175. {
  176. id: '2',
  177. productName: '筆記型電腦',
  178. isQualified: true,
  179. remarks: '經檢查,無損壞',
  180. reportDate: '2024-08-05',
  181. batchNumber: 'NB-240805-003',
  182. inspector: '王檢驗',
  183. testResults: '溫度測試:CPU最高溫度75°C,性能測試:符合預期',
  184. specifications: 'CPU溫度≤85°C,開機時間≤30秒,電池續航≥6小時',
  185. },
  186. ];
  187. // Main component
  188. function EscalationLog(): JSX.Element {
  189. return <QCReportForm reports={dummyReports} />;
  190. };
  191. export default EscalationLog;