"use client";
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 { NEXT_PUBLIC_API_URL } from "@/config/api";
import { clientAuthFetch } from "@/app/utils/clientAuthFetch";
import * as XLSX from "xlsx";
// Simple TabPanel component for conditional rendering
interface TabPanelProps {
children?: React.ReactNode;
index: number;
value: number;
}
function TabPanel(props: TabPanelProps) {
const { children, value, index, ...other } = props;
return (
{value === index && (
{children}
)}
);
}
export default function TestingPage() {
// Tab state
const [tabValue, setTabValue] = useState(0);
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
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) ---
const [grnPreviewReceiptDate, setGrnPreviewReceiptDate] = useState("2026-03-16");
// --- 7. M18 PO Sync by Code ---
const [m18PoCode, setM18PoCode] = useState("");
const [isSyncingM18Po, setIsSyncingM18Po] = useState(false);
const [m18PoSyncResult, setM18PoSyncResult] = useState("");
// 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 () => {
try {
const response = await clientAuthFetch(
`${NEXT_PUBLIC_API_URL}/report/grn-preview-m18?receiptDate=${encodeURIComponent(grnPreviewReceiptDate)}`,
{ method: "GET" },
);
if (response.status === 401 || response.status === 403) return;
if (!response.ok) throw new Error(`Download failed: ${response.status}`);
const data = await response.json();
const rows = Array.isArray(data?.rows) ? data.rows : [];
const ws = XLSX.utils.json_to_sheet(rows);
const wb = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, "GRN Preview");
const xlsxArrayBuffer = XLSX.write(wb, { bookType: "xlsx", type: "array" });
const blob = new Blob([xlsxArrayBuffer], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", `grn-preview-m18-${grnPreviewReceiptDate}.xlsx`);
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
} catch (e) {
console.error("GRN Preview XLSX Download Error:", e);
alert("GRN Preview XLSX download failed. Check console/network.");
}
};
// M18 PO Sync By Code (Section 7)
const handleSyncM18PoByCode = async () => {
if (!m18PoCode.trim()) {
alert("Please enter PO code.");
return;
}
setIsSyncingM18Po(true);
setM18PoSyncResult("");
try {
const response = await clientAuthFetch(
`${NEXT_PUBLIC_API_URL}/m18/test/po-by-code?code=${encodeURIComponent(m18PoCode.trim())}`,
{ method: "GET" },
);
if (response.status === 401 || response.status === 403) return;
const text = await response.text();
setM18PoSyncResult(text);
if (!response.ok) {
alert(`Sync failed: ${response.status}`);
}
} catch (e) {
console.error("M18 PO Sync By Code Error:", e);
alert("M18 PO sync failed. Check console/network.");
} finally {
setIsSyncingM18Po(false);
}
};
// Layout Helper
const Section = ({ title, children }: { title: string, children?: React.ReactNode }) => (
{title}
{children || Waiting for implementation...}
);
return (
Printer Testing
setTscConfig({...tscConfig, ip: e.target.value})} />
setTscConfig({...tscConfig, port: e.target.value})} />
Code
Name
Lot
Expiry
Action
{tscItems.map(row => (
handleItemChange(setTscItems, row.id, 'itemCode', e.target.value)} />
handleItemChange(setTscItems, row.id, 'itemName', e.target.value)} />
handleItemChange(setTscItems, row.id, 'lotNo', e.target.value)} />
handleItemChange(setTscItems, row.id, 'expiryDate', e.target.value)} />
} onClick={() => handleTscPrint(row)}>Print
))}
setDfConfig({...dfConfig, ip: e.target.value})} />
setDfConfig({...dfConfig, port: e.target.value})} />
Code
Name
Lot
Expiry
Action
{dfItems.map(row => (
handleItemChange(setDfItems, row.id, 'itemCode', e.target.value)} />
handleItemChange(setDfItems, row.id, 'itemName', e.target.value)} />
handleItemChange(setDfItems, row.id, 'lotNo', e.target.value)} />
handleItemChange(setDfItems, row.id, 'expiryDate', e.target.value)} />
} onClick={() => handleDfPrint(row)}>Print
))}
Calls /plastic/get-printer6 to generate CoLOS .job bundle.
} onClick={() => setIsPrinterModalOpen(true)}>
Generate CoLOS Files
setLaserConfig({...laserConfig, ip: e.target.value})} />
setLaserConfig({...laserConfig, port: e.target.value})} />
Template
Lot
Exp
Pwr%
Action
{laserItems.map(row => (
handleItemChange(setLaserItems, row.id, 'templateId', e.target.value)} />
handleItemChange(setLaserItems, row.id, 'lotNo', e.target.value)} />
handleItemChange(setLaserItems, row.id, 'expiryDate', e.target.value)} />
handleItemChange(setLaserItems, row.id, 'power', e.target.value)} />
}
onClick={() => handleLaserPrint(row)}
>
Mark
))}
Note: HANS Laser requires pre-saved templates on the controller.
setHansConfig({...hansConfig, ip: e.target.value})}
/>
setHansConfig({...hansConfig, port: e.target.value})}
/>
Ch3 Text (SN)
Ch4 Text (Batch)
Obj3 Name
Obj4 Name
Action
{hansItems.map(row => (
handleItemChange(setHansItems, row.id, 'textChannel3', e.target.value)}
sx={{ minWidth: 180 }}
/>
handleItemChange(setHansItems, row.id, 'textChannel4', e.target.value)}
sx={{ minWidth: 140 }}
/>
handleItemChange(setHansItems, row.id, 'text3ObjectName', e.target.value)}
size="small"
/>
handleItemChange(setHansItems, row.id, 'text4ObjectName', e.target.value)}
size="small"
/>
}
onClick={() => handleHansPrint(row)}
sx={{ minWidth: 80 }}
>
TCP Mark
))}
TCP Push to EZCAD3 (Ch3/Ch4 via E3_SetTextObject) | IP:192.168.76.10:45678 | Backend: /print-laser-tcp
setGrnPreviewReceiptDate(e.target.value)}
InputLabelProps={{ shrink: true }}
/>
}
onClick={handleDownloadGrnPreviewXlsx}
>
Download GRN Preview XLSX
Backend endpoint: /report/grn-preview-m18?receiptDate=YYYY-MM-DD
setM18PoCode(e.target.value)}
placeholder="e.g. PFP002PO26030341"
sx={{ minWidth: 320 }}
/>
Backend endpoint: /m18/test/po-by-code?code=YOUR_CODE
{m18PoSyncResult ? (
) : null}
{/* Dialog for OnPack */}
);
}