FPSMS-frontend
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

338 satır
14 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. {/* Title */}
  107. {/* <Box sx={{ mb: 4 }}>
  108. <Typography variant="h5" fontWeight="bold" color="text.primary">
  109. 品質控制上報系統
  110. </Typography>
  111. <Typography variant="body2" color="text.secondary">
  112. 品質檢測結果上報與管理
  113. </Typography>
  114. </Box> */}
  115. {/* <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', lg: '2fr 1fr' }, gap: 3 }}> */}
  116. <Box sx={{ display: 'grid' }}>
  117. {/* Table Section */}
  118. <Box>
  119. <Typography variant="h6" fontWeight="medium" sx={{ mb: 2 }}>
  120. 上報資料
  121. </Typography>
  122. <TableContainer component={Paper} elevation={2}>
  123. <Table>
  124. <TableHead sx={{ bgcolor: 'grey.50' }}>
  125. {table.getHeaderGroups().map(headerGroup => (
  126. <TableRow key={headerGroup.id}>
  127. {headerGroup.headers.map(header => (
  128. <TableCell
  129. key={header.id}
  130. sx={{
  131. fontSize: '0.75rem',
  132. fontWeight: 'medium',
  133. color: 'text.secondary',
  134. textTransform: 'uppercase',
  135. py: 2,
  136. px: 3,
  137. }}
  138. >
  139. {header.isPlaceholder
  140. ? null
  141. : flexRender(header.column.columnDef.header, header.getContext())}
  142. </TableCell>
  143. ))}
  144. </TableRow>
  145. ))}
  146. </TableHead>
  147. <TableBody>
  148. {table.getRowModel().rows.map(row => (
  149. <TableRow
  150. key={row.id}
  151. onClick={() => handleReportClick(row.original)}
  152. sx={{
  153. cursor: 'pointer',
  154. '&:hover': { bgcolor: 'grey.50' },
  155. bgcolor: selectedReport?.id === row.original.id ? 'primary.light' : 'inherit',
  156. transition: 'background-color 0.15s',
  157. }}
  158. >
  159. {row.getVisibleCells().map(cell => (
  160. <TableCell key={cell.id} sx={{ py: 2, px: 3 }}>
  161. {flexRender(cell.column.columnDef.cell, cell.getContext())}
  162. </TableCell>
  163. ))}
  164. </TableRow>
  165. ))}
  166. </TableBody>
  167. </Table>
  168. </TableContainer>
  169. </Box>
  170. {/* Details Section */}
  171. {/* <Box>
  172. <Typography variant="h6" fontWeight="medium" sx={{ mb: 2 }}>
  173. 詳細資訊
  174. </Typography>
  175. <Card elevation={2} sx={{ minHeight: '300px', bgcolor: 'grey.50' }}>
  176. <CardContent>
  177. {selectedReport ? (
  178. <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
  179. <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, color: 'primary.main', mb: 2 }}>
  180. <DescriptionIcon />
  181. <Typography variant="subtitle1" fontWeight="medium">
  182. 上報詳情
  183. </Typography>
  184. </Box>
  185. <Box>
  186. <Typography variant="body2" color="text.secondary" fontWeight="medium">
  187. 產品名稱:
  188. </Typography>
  189. <Typography variant="body2">{selectedReport.productName}</Typography>
  190. </Box>
  191. <Box>
  192. <Typography variant="body2" color="text.secondary" fontWeight="medium">
  193. 檢測結果:
  194. </Typography>
  195. <Box sx={{ mt: 1 }}>{getQualifiedStatus(selectedReport.isQualified)}</Box>
  196. </Box>
  197. <Box>
  198. <Typography variant="body2" color="text.secondary" fontWeight="medium">
  199. 批次號碼:
  200. </Typography>
  201. <Typography variant="body2">{selectedReport.batchNumber}</Typography>
  202. </Box>
  203. <Box>
  204. <Typography variant="body2" color="text.secondary" fontWeight="medium">
  205. 檢驗員:
  206. </Typography>
  207. <Typography variant="body2">{selectedReport.inspector}</Typography>
  208. </Box>
  209. <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
  210. <CalendarTodayIcon fontSize="small" sx={{ color: 'text.secondary' }} />
  211. <Typography variant="body2">
  212. 上報日期:{selectedReport.reportDate}
  213. </Typography>
  214. </Box>
  215. <Box>
  216. <Typography variant="body2" color="text.secondary" fontWeight="medium" sx={{ mb: 1 }}>
  217. 檢測規格:
  218. </Typography>
  219. <Paper sx={{ p: 1, bgcolor: 'background.paper' }}>
  220. <Typography variant="body2">{selectedReport.specifications}</Typography>
  221. </Paper>
  222. </Box>
  223. <Box>
  224. <Typography variant="body2" color="text.secondary" fontWeight="medium" sx={{ mb: 1 }}>
  225. 檢測結果詳情:
  226. </Typography>
  227. <Paper sx={{ p: 1, bgcolor: 'background.paper' }}>
  228. <Typography variant="body2">{selectedReport.testResults}</Typography>
  229. </Paper>
  230. </Box>
  231. <Box>
  232. <Typography variant="body2" color="text.secondary" fontWeight="medium" sx={{ mb: 1 }}>
  233. 備註 (REMARKS):
  234. </Typography>
  235. <Paper sx={{ p: 1, bgcolor: 'background.paper' }}>
  236. <Typography variant="body2">{selectedReport.remarks}</Typography>
  237. </Paper>
  238. </Box>
  239. </Box>
  240. ) : (
  241. <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
  242. <Typography variant="body2" color="text.secondary">
  243. 請點擊表格中的記錄來查看詳細資訊
  244. </Typography>
  245. </Box>
  246. )}
  247. </CardContent>
  248. </Card>
  249. </Box> */}
  250. </Box>
  251. </Box>
  252. );
  253. }
  254. // Dummy data
  255. const dummyReports = [
  256. // {
  257. // id: '1',
  258. // productName: '智慧手機 Pro Max',
  259. // isQualified: true,
  260. // remarks: '所有檢測項目均符合標準規格',
  261. // reportDate: '2024-08-07',
  262. // batchNumber: 'SPM-240807-001',
  263. // inspector: '張品質',
  264. // testResults: '外觀檢查:無瑕疵,功能測試:100%通過,性能測試:符合標準',
  265. // specifications: '螢幕解析度≥1080p,電池續航≥24小時,防水等級IPX7',
  266. // },
  267. {
  268. id: '2',
  269. productName: '無線藍牙耳機',
  270. isQualified: false,
  271. remarks: '包裝有破損',
  272. reportDate: '2024-08-06',
  273. batchNumber: 'WBE-240806-002',
  274. inspector: '李測試',
  275. testResults: '音質測試:左右聲道音量差異>3dB,不符合標準',
  276. specifications: '頻響範圍20Hz-20kHz,左右聲道音量差≤1dB,電池續航≥8小時',
  277. },
  278. {
  279. id: '3',
  280. productName: '筆記型電腦',
  281. isQualified: true,
  282. remarks: '經檢查,無損壞',
  283. reportDate: '2024-08-05',
  284. batchNumber: 'NB-240805-003',
  285. inspector: '王檢驗',
  286. testResults: '溫度測試:CPU最高溫度75°C,性能測試:符合預期',
  287. specifications: 'CPU溫度≤85°C,開機時間≤30秒,電池續航≥6小時',
  288. },
  289. // {
  290. // id: '4',
  291. // productName: '智慧手錶',
  292. // isQualified: false,
  293. // remarks: '防水測試未通過,密封性能不足',
  294. // reportDate: '2024-08-04',
  295. // batchNumber: 'SW-240804-004',
  296. // inspector: '劉品管',
  297. // testResults: '防水測試:IPX5等級測試失敗,發現進水現象',
  298. // specifications: '防水等級≥IPX7,電池續航≥48小時,心率監測精度≥95%',
  299. // },
  300. // {
  301. // id: '5',
  302. // productName: '平板電腦',
  303. // isQualified: true,
  304. // remarks: '觸控靈敏度優良,顯示效果佳',
  305. // reportDate: '2024-08-03',
  306. // batchNumber: 'TB-240803-005',
  307. // inspector: '陳測試',
  308. // testResults: '觸控測試:反應時間<10ms,顯示測試:色彩準確度98%',
  309. // specifications: '螢幕尺寸10.1吋,解析度2K,觸控反應時間≤15ms',
  310. // },
  311. ];
  312. // Main component
  313. const EscalationLog: React.FC = () => {
  314. return <QCReportForm reports={dummyReports} />;
  315. };
  316. export default EscalationLog;