|
|
@@ -1,19 +1,12 @@ |
|
|
"use client"; |
|
|
"use client"; |
|
|
|
|
|
|
|
|
import React, { useState } from "react"; |
|
|
import React, { useState } from "react"; |
|
|
import { |
|
|
|
|
|
Box, Grid, Paper, Typography, Button, Dialog, DialogTitle, |
|
|
|
|
|
DialogContent, DialogActions, TextField, Stack, Table, |
|
|
|
|
|
TableBody, TableCell, TableContainer, TableHead, TableRow, |
|
|
|
|
|
Tabs, Tab // ← Added for tabs |
|
|
|
|
|
} from "@mui/material"; |
|
|
|
|
|
import { FileDownload, Print, SettingsEthernet, Lan, Router } from "@mui/icons-material"; |
|
|
|
|
|
import dayjs from "dayjs"; |
|
|
|
|
|
|
|
|
import { Box, Paper, Typography, Button, TextField, Stack, Tabs, Tab } from "@mui/material"; |
|
|
|
|
|
import { FileDownload } from "@mui/icons-material"; |
|
|
import { NEXT_PUBLIC_API_URL } from "@/config/api"; |
|
|
import { NEXT_PUBLIC_API_URL } from "@/config/api"; |
|
|
import { clientAuthFetch } from "@/app/utils/clientAuthFetch"; |
|
|
import { clientAuthFetch } from "@/app/utils/clientAuthFetch"; |
|
|
import * as XLSX from "xlsx"; |
|
|
import * as XLSX from "xlsx"; |
|
|
|
|
|
|
|
|
// Simple TabPanel component for conditional rendering |
|
|
|
|
|
interface TabPanelProps { |
|
|
interface TabPanelProps { |
|
|
children?: React.ReactNode; |
|
|
children?: React.ReactNode; |
|
|
index: number; |
|
|
index: number; |
|
|
@@ -30,192 +23,29 @@ function TabPanel(props: TabPanelProps) { |
|
|
aria-labelledby={`simple-tab-${index}`} |
|
|
aria-labelledby={`simple-tab-${index}`} |
|
|
{...other} |
|
|
{...other} |
|
|
> |
|
|
> |
|
|
{value === index && ( |
|
|
|
|
|
<Box sx={{ p: 3 }}> |
|
|
|
|
|
{children} |
|
|
|
|
|
</Box> |
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
{value === index && <Box sx={{ p: 3 }}>{children}</Box>} |
|
|
</div> |
|
|
</div> |
|
|
); |
|
|
); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export default function TestingPage() { |
|
|
export default function TestingPage() { |
|
|
// Tab state |
|
|
|
|
|
const [tabValue, setTabValue] = useState(0); |
|
|
const [tabValue, setTabValue] = useState(0); |
|
|
|
|
|
|
|
|
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { |
|
|
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => { |
|
|
setTabValue(newValue); |
|
|
setTabValue(newValue); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// --- 1. TSC Section States --- |
|
|
|
|
|
const [tscConfig, setTscConfig] = useState({ ip: '192.168.1.100', port: '9100' }); |
|
|
|
|
|
const [tscItems, setTscItems] = useState([ |
|
|
|
|
|
{ id: 1, itemCode: 'FG-001', itemName: 'Yellow Curry Sauce', lotNo: 'LOT-TSC-01', expiryDate: '2025-12-01' }, |
|
|
|
|
|
{ id: 2, itemCode: 'FG-002', itemName: 'Red Curry Paste', lotNo: 'LOT-TSC-02', expiryDate: '2025-12-05' }, |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
// --- 2. DataFlex Section States --- |
|
|
|
|
|
const [dfConfig, setDfConfig] = useState({ ip: '192.168.1.101', port: '9100' }); |
|
|
|
|
|
const [dfItems, setDfItems] = useState([ |
|
|
|
|
|
{ id: 1, itemCode: 'DF-101', itemName: 'Instant Noodle A', lotNo: 'LOT-DF-01', expiryDate: '2026-01-10' }, |
|
|
|
|
|
{ id: 2, itemCode: 'DF-102', itemName: 'Instant Noodle B', lotNo: 'LOT-DF-02', expiryDate: '2026-01-15' }, |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
// --- 3. OnPack Section States --- |
|
|
|
|
|
const [isPrinterModalOpen, setIsPrinterModalOpen] = useState(false); |
|
|
|
|
|
const [printerFormData, setPrinterFormData] = useState({ |
|
|
|
|
|
itemCode: '', |
|
|
|
|
|
lotNo: '', |
|
|
|
|
|
expiryDate: dayjs().format('YYYY-MM-DD'), |
|
|
|
|
|
productName: '' |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// --- 4. Laser Section States --- |
|
|
|
|
|
const [laserConfig, setLaserConfig] = useState({ ip: '192.168.1.102', port: '8080' }); |
|
|
|
|
|
const [laserItems, setLaserItems] = useState([ |
|
|
|
|
|
{ id: 1, templateId: 'JOB_001', lotNo: 'L-LASER-01', expiryDate: '2025-12-31', power: '50' }, |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
// --- 5. HANS600S-M Section States --- |
|
|
|
|
|
const [hansConfig, setHansConfig] = useState({ ip: '192.168.76.10', port: '45678' }); |
|
|
|
|
|
const [hansItems, setHansItems] = useState([ |
|
|
|
|
|
{ |
|
|
|
|
|
id: 1, |
|
|
|
|
|
textChannel3: 'SN-HANS-001-20260117', // channel 3 (e.g. serial / text1) |
|
|
|
|
|
textChannel4: 'BATCH-HK-TEST-OK', // channel 4 (e.g. batch / text2) |
|
|
|
|
|
text3ObjectName: 'Text3', // EZCAD object name for channel 3 |
|
|
|
|
|
text4ObjectName: 'Text4' // EZCAD object name for channel 4 |
|
|
|
|
|
}, |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
// --- 6. GRN Preview (M18) --- |
|
|
|
|
|
|
|
|
// --- 1. GRN Preview (M18) --- |
|
|
const [grnPreviewReceiptDate, setGrnPreviewReceiptDate] = useState("2026-03-16"); |
|
|
const [grnPreviewReceiptDate, setGrnPreviewReceiptDate] = useState("2026-03-16"); |
|
|
// --- 7. M18 PO Sync by Code --- |
|
|
|
|
|
|
|
|
// --- 2. M18 PO Sync by Code --- |
|
|
const [m18PoCode, setM18PoCode] = useState(""); |
|
|
const [m18PoCode, setM18PoCode] = useState(""); |
|
|
const [isSyncingM18Po, setIsSyncingM18Po] = useState(false); |
|
|
const [isSyncingM18Po, setIsSyncingM18Po] = useState(false); |
|
|
const [m18PoSyncResult, setM18PoSyncResult] = useState<string>(""); |
|
|
const [m18PoSyncResult, setM18PoSyncResult] = useState<string>(""); |
|
|
|
|
|
// --- 3. M18 DO Sync by Code --- |
|
|
|
|
|
const [m18DoCode, setM18DoCode] = useState(""); |
|
|
|
|
|
const [isSyncingM18Do, setIsSyncingM18Do] = useState(false); |
|
|
|
|
|
const [m18DoSyncResult, setM18DoSyncResult] = useState<string>(""); |
|
|
|
|
|
|
|
|
// Generic handler for inline table edits |
|
|
|
|
|
const handleItemChange = (setter: any, id: number, field: string, value: string) => { |
|
|
|
|
|
setter((prev: any[]) => prev.map(item => |
|
|
|
|
|
item.id === id ? { ...item, [field]: value } : item |
|
|
|
|
|
)); |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// --- API CALLS --- |
|
|
|
|
|
|
|
|
|
|
|
// TSC Print (Section 1) |
|
|
|
|
|
const handleTscPrint = async (row: any) => { |
|
|
|
|
|
const payload = { ...row, printerIp: tscConfig.ip, printerPort: tscConfig.port }; |
|
|
|
|
|
try { |
|
|
|
|
|
const response = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/plastic/print-tsc`, { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
|
|
|
body: JSON.stringify(payload) |
|
|
|
|
|
}); |
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
|
|
|
if (response.ok) alert(`TSC Print Command Sent for ${row.itemCode}!`); |
|
|
|
|
|
else alert("TSC Print Failed"); |
|
|
|
|
|
} catch (e) { console.error("TSC Error:", e); } |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// DataFlex Print (Section 2) |
|
|
|
|
|
const handleDfPrint = async (row: any) => { |
|
|
|
|
|
const payload = { ...row, printerIp: dfConfig.ip, printerPort: dfConfig.port }; |
|
|
|
|
|
try { |
|
|
|
|
|
const response = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/plastic/print-dataflex`, { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
|
|
|
body: JSON.stringify(payload) |
|
|
|
|
|
}); |
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
|
|
|
if (response.ok) alert(`DataFlex Print Command Sent for ${row.itemCode}!`); |
|
|
|
|
|
else alert("DataFlex Print Failed"); |
|
|
|
|
|
} catch (e) { console.error("DataFlex Error:", e); } |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// OnPack Zip Download (Section 3) |
|
|
|
|
|
const handleDownloadPrintJob = async () => { |
|
|
|
|
|
const params = new URLSearchParams(printerFormData); |
|
|
|
|
|
try { |
|
|
|
|
|
const response = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/plastic/get-printer6?${params.toString()}`, { |
|
|
|
|
|
method: 'GET', |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
|
|
|
if (!response.ok) throw new Error('Download failed'); |
|
|
|
|
|
|
|
|
|
|
|
const blob = await response.blob(); |
|
|
|
|
|
const url = window.URL.createObjectURL(blob); |
|
|
|
|
|
const link = document.createElement('a'); |
|
|
|
|
|
link.href = url; |
|
|
|
|
|
link.setAttribute('download', `${printerFormData.lotNo || 'OnPack'}.zip`); |
|
|
|
|
|
document.body.appendChild(link); |
|
|
|
|
|
link.click(); |
|
|
|
|
|
link.remove(); |
|
|
|
|
|
window.URL.revokeObjectURL(url); |
|
|
|
|
|
|
|
|
|
|
|
setIsPrinterModalOpen(false); |
|
|
|
|
|
} catch (e) { console.error("OnPack Error:", e); } |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Laser Print (Section 4 - original) |
|
|
|
|
|
const handleLaserPrint = async (row: any) => { |
|
|
|
|
|
const payload = { ...row, printerIp: laserConfig.ip, printerPort: laserConfig.port }; |
|
|
|
|
|
try { |
|
|
|
|
|
const response = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/plastic/print-laser`, { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
|
|
|
body: JSON.stringify(payload) |
|
|
|
|
|
}); |
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
|
|
|
if (response.ok) alert(`Laser Command Sent: ${row.templateId}`); |
|
|
|
|
|
} catch (e) { console.error(e); } |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const handleLaserPreview = async (row: any) => { |
|
|
|
|
|
const payload = { ...row, printerIp: laserConfig.ip, printerPort: parseInt(laserConfig.port) }; |
|
|
|
|
|
try { |
|
|
|
|
|
const response = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/plastic/preview-laser`, { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
|
|
|
body: JSON.stringify(payload) |
|
|
|
|
|
}); |
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
|
|
|
if (response.ok) alert("Red light preview active!"); |
|
|
|
|
|
} catch (e) { console.error("Preview Error:", e); } |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// HANS600S-M TCP Print (Section 5) |
|
|
|
|
|
const handleHansPrint = async (row: any) => { |
|
|
|
|
|
const payload = { |
|
|
|
|
|
printerIp: hansConfig.ip, |
|
|
|
|
|
printerPort: hansConfig.port, |
|
|
|
|
|
textChannel3: row.textChannel3, |
|
|
|
|
|
textChannel4: row.textChannel4, |
|
|
|
|
|
text3ObjectName: row.text3ObjectName, |
|
|
|
|
|
text4ObjectName: row.text4ObjectName |
|
|
|
|
|
}; |
|
|
|
|
|
try { |
|
|
|
|
|
const response = await clientAuthFetch(`${NEXT_PUBLIC_API_URL}/plastic/print-laser-tcp`, { |
|
|
|
|
|
method: 'POST', |
|
|
|
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
|
|
|
body: JSON.stringify(payload) |
|
|
|
|
|
}); |
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
|
|
|
const result = await response.text(); |
|
|
|
|
|
if (response.ok) { |
|
|
|
|
|
alert(`HANS600S-M Mark Success: ${result}`); |
|
|
|
|
|
} else { |
|
|
|
|
|
alert(`HANS600S-M Failed: ${result}`); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
console.error("HANS600S-M Error:", e); |
|
|
|
|
|
alert("HANS600S-M Connection Error"); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// GRN Preview CSV Download (Section 6) |
|
|
|
|
|
const handleDownloadGrnPreviewXlsx = async () => { |
|
|
const handleDownloadGrnPreviewXlsx = async () => { |
|
|
try { |
|
|
try { |
|
|
const response = await clientAuthFetch( |
|
|
const response = await clientAuthFetch( |
|
|
@@ -251,7 +81,6 @@ export default function TestingPage() { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// M18 PO Sync By Code (Section 7) |
|
|
|
|
|
const handleSyncM18PoByCode = async () => { |
|
|
const handleSyncM18PoByCode = async () => { |
|
|
if (!m18PoCode.trim()) { |
|
|
if (!m18PoCode.trim()) { |
|
|
alert("Please enter PO code."); |
|
|
alert("Please enter PO code."); |
|
|
@@ -278,258 +107,55 @@ export default function TestingPage() { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// Layout Helper |
|
|
|
|
|
const Section = ({ title, children }: { title: string, children?: React.ReactNode }) => ( |
|
|
|
|
|
<Paper sx={{ p: 3, minHeight: '450px', display: 'flex', flexDirection: 'column' }}> |
|
|
|
|
|
<Typography variant="h5" gutterBottom color="primary" sx={{ borderBottom: '2px solid #f0f0f0', pb: 1, mb: 2 }}> |
|
|
|
|
|
|
|
|
const handleSyncM18DoByCode = async () => { |
|
|
|
|
|
if (!m18DoCode.trim()) { |
|
|
|
|
|
alert("Please enter DO / shop PO code."); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
setIsSyncingM18Do(true); |
|
|
|
|
|
setM18DoSyncResult(""); |
|
|
|
|
|
try { |
|
|
|
|
|
const response = await clientAuthFetch( |
|
|
|
|
|
`${NEXT_PUBLIC_API_URL}/m18/test/do-by-code?code=${encodeURIComponent(m18DoCode.trim())}`, |
|
|
|
|
|
{ method: "GET" }, |
|
|
|
|
|
); |
|
|
|
|
|
if (response.status === 401 || response.status === 403) return; |
|
|
|
|
|
const text = await response.text(); |
|
|
|
|
|
setM18DoSyncResult(text); |
|
|
|
|
|
if (!response.ok) { |
|
|
|
|
|
alert(`Sync failed: ${response.status}`); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
console.error("M18 DO Sync By Code Error:", e); |
|
|
|
|
|
alert("M18 DO sync failed. Check console/network."); |
|
|
|
|
|
} finally { |
|
|
|
|
|
setIsSyncingM18Do(false); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
const Section = ({ title, children }: { title: string; children?: React.ReactNode }) => ( |
|
|
|
|
|
<Paper sx={{ p: 3, minHeight: "450px", display: "flex", flexDirection: "column" }}> |
|
|
|
|
|
<Typography variant="h5" gutterBottom color="primary" sx={{ borderBottom: "2px solid #f0f0f0", pb: 1, mb: 2 }}> |
|
|
{title} |
|
|
{title} |
|
|
</Typography> |
|
|
</Typography> |
|
|
{children || <Typography color="textSecondary" sx={{ m: 'auto' }}>Waiting for implementation...</Typography>} |
|
|
|
|
|
|
|
|
{children || <Typography color="textSecondary" sx={{ m: "auto" }}>Waiting for implementation...</Typography>} |
|
|
</Paper> |
|
|
</Paper> |
|
|
); |
|
|
); |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
<Box sx={{ p: 4 }}> |
|
|
<Box sx={{ p: 4 }}> |
|
|
<Typography variant="h4" sx={{ mb: 4, fontWeight: 'bold' }}>Printer Testing</Typography> |
|
|
|
|
|
|
|
|
|
|
|
<Tabs value={tabValue} onChange={handleTabChange} aria-label="printer sections tabs" centered variant="fullWidth"> |
|
|
|
|
|
<Tab label="1. TSC" /> |
|
|
|
|
|
<Tab label="2. DataFlex" /> |
|
|
|
|
|
<Tab label="3. OnPack" /> |
|
|
|
|
|
<Tab label="4. Laser" /> |
|
|
|
|
|
<Tab label="5. HANS600S-M" /> |
|
|
|
|
|
<Tab label="6. GRN Preview" /> |
|
|
|
|
|
<Tab label="7. M18 PO Sync" /> |
|
|
|
|
|
|
|
|
<Typography variant="h4" sx={{ mb: 4, fontWeight: "bold" }}> |
|
|
|
|
|
Testing |
|
|
|
|
|
</Typography> |
|
|
|
|
|
|
|
|
|
|
|
<Tabs value={tabValue} onChange={handleTabChange} aria-label="testing sections tabs" centered variant="fullWidth"> |
|
|
|
|
|
<Tab label="1. GRN Preview" /> |
|
|
|
|
|
<Tab label="2. M18 PO Sync" /> |
|
|
|
|
|
<Tab label="3. M18 DO Sync" /> |
|
|
</Tabs> |
|
|
</Tabs> |
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={0}> |
|
|
<TabPanel value={tabValue} index={0}> |
|
|
<Section title="1. TSC"> |
|
|
|
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2 }}> |
|
|
|
|
|
<TextField size="small" label="Printer IP" value={tscConfig.ip} onChange={e => setTscConfig({...tscConfig, ip: e.target.value})} /> |
|
|
|
|
|
<TextField size="small" label="Port" value={tscConfig.port} onChange={e => setTscConfig({...tscConfig, port: e.target.value})} /> |
|
|
|
|
|
<SettingsEthernet color="action" /> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
<TableContainer component={Paper} variant="outlined" sx={{ maxHeight: 300 }}> |
|
|
|
|
|
<Table size="small" stickyHeader> |
|
|
|
|
|
<TableHead> |
|
|
|
|
|
<TableRow> |
|
|
|
|
|
<TableCell>Code</TableCell> |
|
|
|
|
|
<TableCell>Name</TableCell> |
|
|
|
|
|
<TableCell>Lot</TableCell> |
|
|
|
|
|
<TableCell>Expiry</TableCell> |
|
|
|
|
|
<TableCell align="center">Action</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
</TableHead> |
|
|
|
|
|
<TableBody> |
|
|
|
|
|
{tscItems.map(row => ( |
|
|
|
|
|
<TableRow key={row.id}> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.itemCode} onChange={e => handleItemChange(setTscItems, row.id, 'itemCode', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.itemName} onChange={e => handleItemChange(setTscItems, row.id, 'itemName', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.lotNo} onChange={e => handleItemChange(setTscItems, row.id, 'lotNo', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" type="date" value={row.expiryDate} onChange={e => handleItemChange(setTscItems, row.id, 'expiryDate', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell align="center"><Button variant="contained" size="small" startIcon={<Print />} onClick={() => handleTscPrint(row)}>Print</Button></TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
))} |
|
|
|
|
|
</TableBody> |
|
|
|
|
|
</Table> |
|
|
|
|
|
</TableContainer> |
|
|
|
|
|
</Section> |
|
|
|
|
|
</TabPanel> |
|
|
|
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={1}> |
|
|
|
|
|
<Section title="2. DataFlex"> |
|
|
|
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2 }}> |
|
|
|
|
|
<TextField size="small" label="Printer IP" value={dfConfig.ip} onChange={e => setDfConfig({...dfConfig, ip: e.target.value})} /> |
|
|
|
|
|
<TextField size="small" label="Port" value={dfConfig.port} onChange={e => setDfConfig({...dfConfig, port: e.target.value})} /> |
|
|
|
|
|
<Lan color="action" /> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
<TableContainer component={Paper} variant="outlined" sx={{ maxHeight: 300 }}> |
|
|
|
|
|
<Table size="small" stickyHeader> |
|
|
|
|
|
<TableHead> |
|
|
|
|
|
<TableRow> |
|
|
|
|
|
<TableCell>Code</TableCell> |
|
|
|
|
|
<TableCell>Name</TableCell> |
|
|
|
|
|
<TableCell>Lot</TableCell> |
|
|
|
|
|
<TableCell>Expiry</TableCell> |
|
|
|
|
|
<TableCell align="center">Action</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
</TableHead> |
|
|
|
|
|
<TableBody> |
|
|
|
|
|
{dfItems.map(row => ( |
|
|
|
|
|
<TableRow key={row.id}> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.itemCode} onChange={e => handleItemChange(setDfItems, row.id, 'itemCode', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.itemName} onChange={e => handleItemChange(setDfItems, row.id, 'itemName', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.lotNo} onChange={e => handleItemChange(setDfItems, row.id, 'lotNo', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" type="date" value={row.expiryDate} onChange={e => handleItemChange(setDfItems, row.id, 'expiryDate', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell align="center"><Button variant="contained" color="secondary" size="small" startIcon={<Print />} onClick={() => handleDfPrint(row)}>Print</Button></TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
))} |
|
|
|
|
|
</TableBody> |
|
|
|
|
|
</Table> |
|
|
|
|
|
</TableContainer> |
|
|
|
|
|
</Section> |
|
|
|
|
|
</TabPanel> |
|
|
|
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={2}> |
|
|
|
|
|
<Section title="3. OnPack"> |
|
|
|
|
|
<Box sx={{ m: 'auto', textAlign: 'center' }}> |
|
|
|
|
|
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}> |
|
|
|
|
|
Calls /plastic/get-printer6 to generate CoLOS .job bundle. |
|
|
|
|
|
</Typography> |
|
|
|
|
|
<Button variant="contained" color="success" size="large" startIcon={<FileDownload />} onClick={() => setIsPrinterModalOpen(true)}> |
|
|
|
|
|
Generate CoLOS Files |
|
|
|
|
|
</Button> |
|
|
|
|
|
</Box> |
|
|
|
|
|
</Section> |
|
|
|
|
|
</TabPanel> |
|
|
|
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={3}> |
|
|
|
|
|
<Section title="4. Laser"> |
|
|
|
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2 }}> |
|
|
|
|
|
<TextField size="small" label="Laser IP" value={laserConfig.ip} onChange={e => setLaserConfig({...laserConfig, ip: e.target.value})} /> |
|
|
|
|
|
<TextField size="small" label="Port" value={laserConfig.port} onChange={e => setLaserConfig({...laserConfig, port: e.target.value})} /> |
|
|
|
|
|
<Router color="action" /> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
|
|
|
|
|
|
<TableContainer component={Paper} variant="outlined" sx={{ maxHeight: 300 }}> |
|
|
|
|
|
<Table size="small" stickyHeader> |
|
|
|
|
|
<TableHead> |
|
|
|
|
|
<TableRow> |
|
|
|
|
|
<TableCell>Template</TableCell> |
|
|
|
|
|
<TableCell>Lot</TableCell> |
|
|
|
|
|
<TableCell>Exp</TableCell> |
|
|
|
|
|
<TableCell>Pwr%</TableCell> |
|
|
|
|
|
<TableCell align="center">Action</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
</TableHead> |
|
|
|
|
|
<TableBody> |
|
|
|
|
|
{laserItems.map(row => ( |
|
|
|
|
|
<TableRow key={row.id}> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.templateId} onChange={e => handleItemChange(setLaserItems, row.id, 'templateId', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.lotNo} onChange={e => handleItemChange(setLaserItems, row.id, 'lotNo', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" type="date" value={row.expiryDate} onChange={e => handleItemChange(setLaserItems, row.id, 'expiryDate', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell><TextField variant="standard" value={row.power} sx={{ width: 40 }} onChange={e => handleItemChange(setLaserItems, row.id, 'power', e.target.value)} /></TableCell> |
|
|
|
|
|
<TableCell align="center"> |
|
|
|
|
|
<Stack direction="row" spacing={1} justifyContent="center"> |
|
|
|
|
|
<Button |
|
|
|
|
|
variant="outlined" |
|
|
|
|
|
color="info" |
|
|
|
|
|
size="small" |
|
|
|
|
|
onClick={() => handleLaserPreview(row)} |
|
|
|
|
|
> |
|
|
|
|
|
Preview |
|
|
|
|
|
</Button> |
|
|
|
|
|
<Button |
|
|
|
|
|
variant="contained" |
|
|
|
|
|
color="warning" |
|
|
|
|
|
size="small" |
|
|
|
|
|
startIcon={<Print />} |
|
|
|
|
|
onClick={() => handleLaserPrint(row)} |
|
|
|
|
|
> |
|
|
|
|
|
Mark |
|
|
|
|
|
</Button> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
))} |
|
|
|
|
|
</TableBody> |
|
|
|
|
|
</Table> |
|
|
|
|
|
</TableContainer> |
|
|
|
|
|
<Typography variant="caption" sx={{ mt: 2, display: 'block', color: 'text.secondary' }}> |
|
|
|
|
|
Note: HANS Laser requires pre-saved templates on the controller. |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</Section> |
|
|
|
|
|
</TabPanel> |
|
|
|
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={4}> |
|
|
|
|
|
<Section title="5. HANS600S-M"> |
|
|
|
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2 }}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
size="small" |
|
|
|
|
|
label="Laser IP" |
|
|
|
|
|
value={hansConfig.ip} |
|
|
|
|
|
onChange={e => setHansConfig({...hansConfig, ip: e.target.value})} |
|
|
|
|
|
/> |
|
|
|
|
|
<TextField |
|
|
|
|
|
size="small" |
|
|
|
|
|
label="Port" |
|
|
|
|
|
value={hansConfig.port} |
|
|
|
|
|
onChange={e => setHansConfig({...hansConfig, port: e.target.value})} |
|
|
|
|
|
/> |
|
|
|
|
|
<Router color="action" sx={{ ml: 'auto' }} /> |
|
|
|
|
|
</Stack> |
|
|
|
|
|
|
|
|
|
|
|
<TableContainer component={Paper} variant="outlined" sx={{ maxHeight: 300 }}> |
|
|
|
|
|
<Table size="small" stickyHeader> |
|
|
|
|
|
<TableHead> |
|
|
|
|
|
<TableRow> |
|
|
|
|
|
<TableCell>Ch3 Text (SN)</TableCell> |
|
|
|
|
|
<TableCell>Ch4 Text (Batch)</TableCell> |
|
|
|
|
|
<TableCell>Obj3 Name</TableCell> |
|
|
|
|
|
<TableCell>Obj4 Name</TableCell> |
|
|
|
|
|
<TableCell align="center">Action</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
</TableHead> |
|
|
|
|
|
<TableBody> |
|
|
|
|
|
{hansItems.map(row => ( |
|
|
|
|
|
<TableRow key={row.id}> |
|
|
|
|
|
<TableCell> |
|
|
|
|
|
<TextField |
|
|
|
|
|
variant="standard" |
|
|
|
|
|
value={row.textChannel3} |
|
|
|
|
|
onChange={e => handleItemChange(setHansItems, row.id, 'textChannel3', e.target.value)} |
|
|
|
|
|
sx={{ minWidth: 180 }} |
|
|
|
|
|
/> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
<TableCell> |
|
|
|
|
|
<TextField |
|
|
|
|
|
variant="standard" |
|
|
|
|
|
value={row.textChannel4} |
|
|
|
|
|
onChange={e => handleItemChange(setHansItems, row.id, 'textChannel4', e.target.value)} |
|
|
|
|
|
sx={{ minWidth: 140 }} |
|
|
|
|
|
/> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
<TableCell> |
|
|
|
|
|
<TextField |
|
|
|
|
|
variant="standard" |
|
|
|
|
|
value={row.text3ObjectName} |
|
|
|
|
|
onChange={e => handleItemChange(setHansItems, row.id, 'text3ObjectName', e.target.value)} |
|
|
|
|
|
size="small" |
|
|
|
|
|
/> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
<TableCell> |
|
|
|
|
|
<TextField |
|
|
|
|
|
variant="standard" |
|
|
|
|
|
value={row.text4ObjectName} |
|
|
|
|
|
onChange={e => handleItemChange(setHansItems, row.id, 'text4ObjectName', e.target.value)} |
|
|
|
|
|
size="small" |
|
|
|
|
|
/> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
<TableCell align="center"> |
|
|
|
|
|
<Button |
|
|
|
|
|
variant="contained" |
|
|
|
|
|
color="error" |
|
|
|
|
|
size="small" |
|
|
|
|
|
startIcon={<Print />} |
|
|
|
|
|
onClick={() => handleHansPrint(row)} |
|
|
|
|
|
sx={{ minWidth: 80 }} |
|
|
|
|
|
> |
|
|
|
|
|
TCP Mark |
|
|
|
|
|
</Button> |
|
|
|
|
|
</TableCell> |
|
|
|
|
|
</TableRow> |
|
|
|
|
|
))} |
|
|
|
|
|
</TableBody> |
|
|
|
|
|
</Table> |
|
|
|
|
|
</TableContainer> |
|
|
|
|
|
<Typography variant="caption" sx={{ mt: 2, display: 'block', color: 'text.secondary', fontSize: '0.75rem' }}> |
|
|
|
|
|
TCP Push to EZCAD3 (Ch3/Ch4 via E3_SetTextObject) | IP:192.168.76.10:45678 | Backend: /print-laser-tcp |
|
|
|
|
|
</Typography> |
|
|
|
|
|
</Section> |
|
|
|
|
|
</TabPanel> |
|
|
|
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={5}> |
|
|
|
|
|
<Section title="6. GRN Preview (M18)"> |
|
|
|
|
|
|
|
|
<Section title="1. GRN Preview (M18)"> |
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center" }}> |
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center" }}> |
|
|
<TextField |
|
|
<TextField |
|
|
size="small" |
|
|
size="small" |
|
|
@@ -555,8 +181,8 @@ export default function TestingPage() { |
|
|
</Section> |
|
|
</Section> |
|
|
</TabPanel> |
|
|
</TabPanel> |
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={6}> |
|
|
|
|
|
<Section title="7. M18 PO Sync by Code"> |
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={1}> |
|
|
|
|
|
<Section title="2. M18 PO Sync by Code"> |
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center" }}> |
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center" }}> |
|
|
<TextField |
|
|
<TextField |
|
|
size="small" |
|
|
size="small" |
|
|
@@ -566,12 +192,7 @@ export default function TestingPage() { |
|
|
placeholder="e.g. PFP002PO26030341" |
|
|
placeholder="e.g. PFP002PO26030341" |
|
|
sx={{ minWidth: 320 }} |
|
|
sx={{ minWidth: 320 }} |
|
|
/> |
|
|
/> |
|
|
<Button |
|
|
|
|
|
variant="contained" |
|
|
|
|
|
color="primary" |
|
|
|
|
|
onClick={handleSyncM18PoByCode} |
|
|
|
|
|
disabled={isSyncingM18Po} |
|
|
|
|
|
> |
|
|
|
|
|
|
|
|
<Button variant="contained" color="primary" onClick={handleSyncM18PoByCode} disabled={isSyncingM18Po}> |
|
|
{isSyncingM18Po ? "Syncing..." : "Sync PO from M18"} |
|
|
{isSyncingM18Po ? "Syncing..." : "Sync PO from M18"} |
|
|
</Button> |
|
|
</Button> |
|
|
</Stack> |
|
|
</Stack> |
|
|
@@ -592,22 +213,37 @@ export default function TestingPage() { |
|
|
</Section> |
|
|
</Section> |
|
|
</TabPanel> |
|
|
</TabPanel> |
|
|
|
|
|
|
|
|
{/* Dialog for OnPack */} |
|
|
|
|
|
<Dialog open={isPrinterModalOpen} onClose={() => setIsPrinterModalOpen(false)} fullWidth maxWidth="sm"> |
|
|
|
|
|
<DialogTitle sx={{ bgcolor: 'success.main', color: 'white' }}>OnPack Printer Job Details</DialogTitle> |
|
|
|
|
|
<DialogContent sx={{ mt: 2 }}> |
|
|
|
|
|
<Stack spacing={3}> |
|
|
|
|
|
<TextField label="Item Code" fullWidth value={printerFormData.itemCode} onChange={(e) => setPrinterFormData({ ...printerFormData, itemCode: e.target.value })} /> |
|
|
|
|
|
<TextField label="Lot Number" fullWidth value={printerFormData.lotNo} onChange={(e) => setPrinterFormData({ ...printerFormData, lotNo: e.target.value })} /> |
|
|
|
|
|
<TextField label="Product Name" fullWidth value={printerFormData.productName} onChange={(e) => setPrinterFormData({ ...printerFormData, productName: e.target.value })} /> |
|
|
|
|
|
<TextField label="Expiry Date" type="date" fullWidth InputLabelProps={{ shrink: true }} value={printerFormData.expiryDate} onChange={(e) => setPrinterFormData({ ...printerFormData, expiryDate: e.target.value })} /> |
|
|
|
|
|
|
|
|
<TabPanel value={tabValue} index={2}> |
|
|
|
|
|
<Section title="3. M18 DO Sync by Code"> |
|
|
|
|
|
<Stack direction="row" spacing={2} sx={{ mb: 2, alignItems: "center" }}> |
|
|
|
|
|
<TextField |
|
|
|
|
|
size="small" |
|
|
|
|
|
label="DO / Shop PO Code" |
|
|
|
|
|
value={m18DoCode} |
|
|
|
|
|
onChange={(e) => setM18DoCode(e.target.value)} |
|
|
|
|
|
placeholder="e.g. same document code as M18 shop PO" |
|
|
|
|
|
sx={{ minWidth: 320 }} |
|
|
|
|
|
/> |
|
|
|
|
|
<Button variant="contained" color="primary" onClick={handleSyncM18DoByCode} disabled={isSyncingM18Do}> |
|
|
|
|
|
{isSyncingM18Do ? "Syncing..." : "Sync DO from M18"} |
|
|
|
|
|
</Button> |
|
|
</Stack> |
|
|
</Stack> |
|
|
</DialogContent> |
|
|
|
|
|
<DialogActions sx={{ p: 3 }}> |
|
|
|
|
|
<Button onClick={() => setIsPrinterModalOpen(false)} variant="outlined" color="inherit">Cancel</Button> |
|
|
|
|
|
<Button variant="contained" color="success" onClick={handleDownloadPrintJob}>Generate & Download</Button> |
|
|
|
|
|
</DialogActions> |
|
|
|
|
|
</Dialog> |
|
|
|
|
|
|
|
|
<Typography variant="body2" color="textSecondary"> |
|
|
|
|
|
Backend endpoint: <code>/m18/test/do-by-code?code=YOUR_CODE</code> |
|
|
|
|
|
</Typography> |
|
|
|
|
|
{m18DoSyncResult ? ( |
|
|
|
|
|
<TextField |
|
|
|
|
|
fullWidth |
|
|
|
|
|
multiline |
|
|
|
|
|
minRows={4} |
|
|
|
|
|
margin="normal" |
|
|
|
|
|
label="Sync Result" |
|
|
|
|
|
value={m18DoSyncResult} |
|
|
|
|
|
InputProps={{ readOnly: true }} |
|
|
|
|
|
/> |
|
|
|
|
|
) : null} |
|
|
|
|
|
</Section> |
|
|
|
|
|
</TabPanel> |
|
|
</Box> |
|
|
</Box> |
|
|
); |
|
|
); |
|
|
} |
|
|
|
|
|
|
|
|
} |