瀏覽代碼

quick add escalation tab component

master
cyril.tsui 2 週之前
父節點
當前提交
43afd5cd3c
共有 2 個文件被更改,包括 503 次插入0 次删除
  1. +165
    -0
      src/components/PoDetail/EscalationComponent.tsx
  2. +338
    -0
      src/components/PoDetail/EscalationLog.tsx

+ 165
- 0
src/components/PoDetail/EscalationComponent.tsx 查看文件

@@ -0,0 +1,165 @@
import React, { useState, ChangeEvent, FormEvent } from 'react';
import {
Box,
Button,
Collapse,
FormControl,
InputLabel,
Select,
MenuItem,
TextField,
Checkbox,
FormControlLabel,
Paper,
Typography,
RadioGroup,
Radio,
} from '@mui/material';
import { SelectChangeEvent } from '@mui/material/Select';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';

interface NameOption {
value: string;
label: string;
}

interface FormData {
name: string;
quantity: string;
message: string;
}

function CollapsibleSelectForm(): JSX.Element {
const [isCollapsed, setIsCollapsed] = useState<boolean>(false);
const [formData, setFormData] = useState<FormData>({
name: '',
quantity: '',
message: '',
});

const nameOptions: NameOption[] = [
{ value: '', label: '請選擇姓名...' },
{ value: 'john', label: '張大明' },
{ value: 'jane', label: '李小美' },
{ value: 'mike', label: '王志強' },
{ value: 'sarah', label: '陳淑華' },
{ value: 'david', label: '林建國' },
];

const handleInputChange = (
e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | SelectChangeEvent<string>
): void => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};

const handleSubmit = (e: FormEvent<HTMLFormElement>): void => {
e.preventDefault();
console.log('表單已提交:', formData);
// 處理表單提交
};

const handleCollapseToggle = (e: ChangeEvent<HTMLInputElement>): void => {
setIsCollapsed(e.target.checked);
};

return (
// <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}>
<Paper elevation={3} sx={{ mx: 'auto', p: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}>
<FormControlLabel
control={
<Checkbox
checked={isCollapsed}
onChange={handleCollapseToggle}
color="primary"
/>
}
label={
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant="body1">上報結果</Typography>
{isCollapsed ? (
<ExpandLessIcon sx={{ ml: 1 }} />
) : (
<ExpandMoreIcon sx={{ ml: 1 }} />
)}
</Box>
}
/>
</Box>

<Collapse in={!isCollapsed}>
<Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<FormControl>
<RadioGroup
row
aria-labelledby="demo-radio-buttons-group-label"
defaultValue="pass"
name="radio-buttons-group"
>
<FormControlLabel value="pass" control={<Radio />} label="合格" />
<FormControlLabel value="fail" control={<Radio />} label="不合格" />
</RadioGroup>
</FormControl>
<FormControl fullWidth>
<InputLabel id="name-label">姓名</InputLabel>
<Select
labelId="name-label"
id="name"
name="name"
value={formData.name}
label="姓名"
onChange={handleInputChange}
>
{nameOptions.map((option: NameOption) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>

<TextField
fullWidth
id="quantity"
name="quantity"
label="數量"
type="number"
value={formData.quantity}
onChange={handleInputChange}
InputProps={{ inputProps: { min: 1 } }}
placeholder="請輸入數量"
/>

<TextField
fullWidth
id="message"
name="message"
label="備註"
multiline
rows={4}
value={formData.message}
onChange={handleInputChange}
placeholder="請輸入您的備註"
/>

<Button
type="submit"
variant="contained"
color="primary"
fullWidth
sx={{ mt: 1 }}
>
提交
</Button>
</Box>
</Collapse>
</Paper>
);
}

export default CollapsibleSelectForm;

+ 338
- 0
src/components/PoDetail/EscalationLog.tsx 查看文件

@@ -0,0 +1,338 @@
import React, { useState } from 'react';
import {
Box,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
Chip,
Card,
CardContent,
} from '@mui/material';
import {
useReactTable,
getCoreRowModel,
createColumnHelper,
flexRender,
} from '@tanstack/react-table';
import DescriptionIcon from '@mui/icons-material/Description';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import CancelIcon from '@mui/icons-material/Cancel';
import CalendarTodayIcon from '@mui/icons-material/CalendarToday';

interface QCReport {
id: string;
productName: string;
isQualified: boolean;
remarks: string;
reportDate: string;
batchNumber: string;
inspector: string;
testResults: string;
specifications: string;
}

interface QCReportFormProps {
reports: QCReport[];
}

function QCReportForm({ reports }: QCReportFormProps): JSX.Element {
const [selectedReport, setSelectedReport] = useState<QCReport | null>(null);

const handleReportClick = (report: QCReport): void => {
setSelectedReport(report);
};

const getQualifiedStatus = (isQualified: boolean) => {
return isQualified ? (
<Chip
icon={<CheckCircleIcon />}
label="合格"
color="success"
size="small"
sx={{ fontWeight: 'medium' }}
/>
) : (
<Chip
icon={<CancelIcon />}
label="不合格"
color="error"
size="small"
sx={{ fontWeight: 'medium' }}
/>
);
};

const columnHelper = createColumnHelper<QCReport>();

const columns = [
// columnHelper.accessor('productName', {
// header: '產品',
// cell: info => (
// <Typography variant="body2" fontWeight="medium">
// {info.getValue()}
// </Typography>
// ),
// }),
columnHelper.accessor('isQualified', {
header: '合格',
cell: info => getQualifiedStatus(info.getValue()),
}),
columnHelper.accessor('remarks', {
header: '備註',
cell: info => (
<Typography
variant="body2"
color="text.secondary"
sx={{ maxWidth: '200px', overflow: 'hidden', textOverflow: 'ellipsis' }}
>
{info.getValue()}
</Typography>
),
}),
columnHelper.accessor('reportDate', {
header: '上報日期',
cell: info => (
<Typography variant="body2" color="text.secondary">
{info.getValue()}
</Typography>
),
}),
];

const table = useReactTable({
data: reports,
columns,
getCoreRowModel: getCoreRowModel(),
});

return (
<Box sx={{ maxWidth: '1200px', mx: 'auto', p: 3 }}>
{/* Title */}
{/* <Box sx={{ mb: 4 }}>
<Typography variant="h5" fontWeight="bold" color="text.primary">
品質控制上報系統
</Typography>
<Typography variant="body2" color="text.secondary">
品質檢測結果上報與管理
</Typography>
</Box> */}

{/* <Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', lg: '2fr 1fr' }, gap: 3 }}> */}
<Box sx={{ display: 'grid' }}>
{/* Table Section */}
<Box>
<Typography variant="h6" fontWeight="medium" sx={{ mb: 2 }}>
上報資料
</Typography>
<TableContainer component={Paper} elevation={2}>
<Table>
<TableHead sx={{ bgcolor: 'grey.50' }}>
{table.getHeaderGroups().map(headerGroup => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map(header => (
<TableCell
key={header.id}
sx={{
fontSize: '0.75rem',
fontWeight: 'medium',
color: 'text.secondary',
textTransform: 'uppercase',
py: 2,
px: 3,
}}
>
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableHead>
<TableBody>
{table.getRowModel().rows.map(row => (
<TableRow
key={row.id}
onClick={() => handleReportClick(row.original)}
sx={{
cursor: 'pointer',
'&:hover': { bgcolor: 'grey.50' },
bgcolor: selectedReport?.id === row.original.id ? 'primary.light' : 'inherit',
transition: 'background-color 0.15s',
}}
>
{row.getVisibleCells().map(cell => (
<TableCell key={cell.id} sx={{ py: 2, px: 3 }}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Box>

{/* Details Section */}
{/* <Box>
<Typography variant="h6" fontWeight="medium" sx={{ mb: 2 }}>
詳細資訊
</Typography>
<Card elevation={2} sx={{ minHeight: '300px', bgcolor: 'grey.50' }}>
<CardContent>
{selectedReport ? (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, color: 'primary.main', mb: 2 }}>
<DescriptionIcon />
<Typography variant="subtitle1" fontWeight="medium">
上報詳情
</Typography>
</Box>

<Box>
<Typography variant="body2" color="text.secondary" fontWeight="medium">
產品名稱:
</Typography>
<Typography variant="body2">{selectedReport.productName}</Typography>
</Box>

<Box>
<Typography variant="body2" color="text.secondary" fontWeight="medium">
檢測結果:
</Typography>
<Box sx={{ mt: 1 }}>{getQualifiedStatus(selectedReport.isQualified)}</Box>
</Box>

<Box>
<Typography variant="body2" color="text.secondary" fontWeight="medium">
批次號碼:
</Typography>
<Typography variant="body2">{selectedReport.batchNumber}</Typography>
</Box>

<Box>
<Typography variant="body2" color="text.secondary" fontWeight="medium">
檢驗員:
</Typography>
<Typography variant="body2">{selectedReport.inspector}</Typography>
</Box>

<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<CalendarTodayIcon fontSize="small" sx={{ color: 'text.secondary' }} />
<Typography variant="body2">
上報日期:{selectedReport.reportDate}
</Typography>
</Box>

<Box>
<Typography variant="body2" color="text.secondary" fontWeight="medium" sx={{ mb: 1 }}>
檢測規格:
</Typography>
<Paper sx={{ p: 1, bgcolor: 'background.paper' }}>
<Typography variant="body2">{selectedReport.specifications}</Typography>
</Paper>
</Box>

<Box>
<Typography variant="body2" color="text.secondary" fontWeight="medium" sx={{ mb: 1 }}>
檢測結果詳情:
</Typography>
<Paper sx={{ p: 1, bgcolor: 'background.paper' }}>
<Typography variant="body2">{selectedReport.testResults}</Typography>
</Paper>
</Box>

<Box>
<Typography variant="body2" color="text.secondary" fontWeight="medium" sx={{ mb: 1 }}>
備註 (REMARKS):
</Typography>
<Paper sx={{ p: 1, bgcolor: 'background.paper' }}>
<Typography variant="body2">{selectedReport.remarks}</Typography>
</Paper>
</Box>
</Box>
) : (
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
<Typography variant="body2" color="text.secondary">
請點擊表格中的記錄來查看詳細資訊
</Typography>
</Box>
)}
</CardContent>
</Card>
</Box> */}
</Box>
</Box>
);
}

// Dummy data
const dummyReports = [
// {
// id: '1',
// productName: '智慧手機 Pro Max',
// isQualified: true,
// remarks: '所有檢測項目均符合標準規格',
// reportDate: '2024-08-07',
// batchNumber: 'SPM-240807-001',
// inspector: '張品質',
// testResults: '外觀檢查:無瑕疵,功能測試:100%通過,性能測試:符合標準',
// specifications: '螢幕解析度≥1080p,電池續航≥24小時,防水等級IPX7',
// },
{
id: '2',
productName: '無線藍牙耳機',
isQualified: false,
remarks: '包裝有破損',
reportDate: '2024-08-06',
batchNumber: 'WBE-240806-002',
inspector: '李測試',
testResults: '音質測試:左右聲道音量差異>3dB,不符合標準',
specifications: '頻響範圍20Hz-20kHz,左右聲道音量差≤1dB,電池續航≥8小時',
},
{
id: '3',
productName: '筆記型電腦',
isQualified: true,
remarks: '經檢查,無損壞',
reportDate: '2024-08-05',
batchNumber: 'NB-240805-003',
inspector: '王檢驗',
testResults: '溫度測試:CPU最高溫度75°C,性能測試:符合預期',
specifications: 'CPU溫度≤85°C,開機時間≤30秒,電池續航≥6小時',
},
// {
// id: '4',
// productName: '智慧手錶',
// isQualified: false,
// remarks: '防水測試未通過,密封性能不足',
// reportDate: '2024-08-04',
// batchNumber: 'SW-240804-004',
// inspector: '劉品管',
// testResults: '防水測試:IPX5等級測試失敗,發現進水現象',
// specifications: '防水等級≥IPX7,電池續航≥48小時,心率監測精度≥95%',
// },
// {
// id: '5',
// productName: '平板電腦',
// isQualified: true,
// remarks: '觸控靈敏度優良,顯示效果佳',
// reportDate: '2024-08-03',
// batchNumber: 'TB-240803-005',
// inspector: '陳測試',
// testResults: '觸控測試:反應時間<10ms,顯示測試:色彩準確度98%',
// specifications: '螢幕尺寸10.1吋,解析度2K,觸控反應時間≤15ms',
// },
];

// Main component
const EscalationLog: React.FC = () => {
return <QCReportForm reports={dummyReports} />;
};

export default EscalationLog;

Loading…
取消
儲存