Explorar el Código

Merge branch 'MergeProblem1' of http://svn.2fi-solutions.com:8300/derek/FPSMS-frontend into MergeProblem1

MergeProblem1
CANCERYS\kw093 hace 2 semanas
padre
commit
d240e23bab
Se han modificado 12 ficheros con 417 adiciones y 83 borrados
  1. +191
    -21
      src/app/(main)/testing/page.tsx
  2. +1
    -1
      src/components/CreateItem/CreateItem.tsx
  3. +15
    -26
      src/components/DashboardPage/DashboardPage.tsx
  4. +163
    -0
      src/components/DashboardPage/goodsReceiptStatus/GoodsReceiptStatus.tsx
  5. +1
    -0
      src/components/DashboardPage/goodsReceiptStatus/index.ts
  6. +0
    -5
      src/components/DashboardPage/truckSchedule/TruckScheduleDashboard.tsx
  7. +13
    -26
      src/components/ItemsSearch/ItemsSearch.tsx
  8. +1
    -1
      src/components/NavigationContent/NavigationContent.tsx
  9. +15
    -0
      src/config/reportConfig.ts
  10. +8
    -1
      src/i18n/en/dashboard.json
  11. +8
    -1
      src/i18n/zh/dashboard.json
  12. +1
    -1
      src/i18n/zh/items.json

+ 191
- 21
src/app/(main)/testing/page.tsx Ver fichero

@@ -4,13 +4,47 @@ import React, { useState } from "react";
import {
Box, Grid, Paper, Typography, Button, Dialog, DialogTitle,
DialogContent, DialogActions, TextField, Stack, Table,
TableBody, TableCell, TableContainer, TableHead, TableRow
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";

// 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 (
<div
role="tabpanel"
hidden={value !== index}
id={`simple-tabpanel-${index}`}
aria-labelledby={`simple-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
{children}
</Box>
)}
</div>
);
}

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([
@@ -35,10 +69,22 @@ export default function TestingPage() {
});

// --- 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' },
]);
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
},
]);

// Generic handler for inline table edits
const handleItemChange = (setter: any, id: number, field: string, value: string) => {
@@ -105,6 +151,7 @@ const [laserItems, setLaserItems] = useState([
} catch (e) { console.error("OnPack Error:", e); }
};

// Laser Print (Section 4 - original)
const handleLaserPrint = async (row: any) => {
const token = localStorage.getItem("accessToken");
const payload = { ...row, printerIp: laserConfig.ip, printerPort: laserConfig.port };
@@ -122,7 +169,6 @@ const [laserItems, setLaserItems] = useState([
const token = localStorage.getItem("accessToken");
const payload = { ...row, printerIp: laserConfig.ip, printerPort: parseInt(laserConfig.port) };
try {
// We'll create this endpoint in the backend next
const response = await fetch(`${NEXT_PUBLIC_API_URL}/plastic/preview-laser`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
@@ -132,24 +178,58 @@ const [laserItems, setLaserItems] = useState([
} catch (e) { console.error("Preview Error:", e); }
};

// HANS600S-M TCP Print (Section 5)
const handleHansPrint = async (row: any) => {
const token = localStorage.getItem("accessToken");
const payload = {
printerIp: hansConfig.ip,
printerPort: hansConfig.port,
textChannel3: row.textChannel3,
textChannel4: row.textChannel4,
text3ObjectName: row.text3ObjectName,
text4ObjectName: row.text4ObjectName
};
try {
const response = await fetch(`${NEXT_PUBLIC_API_URL}/plastic/print-laser-tcp`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
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");
}
};

// Layout Helper
const Section = ({ title, children }: { title: string, children?: React.ReactNode }) => (
<Grid item xs={12} md={6}>
<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}
</Typography>
{children || <Typography color="textSecondary" sx={{ m: 'auto' }}>Waiting for implementation...</Typography>}
</Paper>
</Grid>
<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}
</Typography>
{children || <Typography color="textSecondary" sx={{ m: 'auto' }}>Waiting for implementation...</Typography>}
</Paper>
);

return (
<Box sx={{ p: 4 }}>
<Typography variant="h4" sx={{ mb: 4, fontWeight: 'bold' }}>Printer Testing Dashboard</Typography>
<Typography variant="h4" sx={{ mb: 4, fontWeight: 'bold' }}>Printer Testing</Typography>
<Grid container spacing={3}>
{/* 1. TSC Section */}
<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" />
</Tabs>

<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})} />
@@ -181,8 +261,9 @@ const [laserItems, setLaserItems] = useState([
</Table>
</TableContainer>
</Section>
</TabPanel>

{/* 2. DataFlex Section */}
<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})} />
@@ -214,8 +295,9 @@ const [laserItems, setLaserItems] = useState([
</Table>
</TableContainer>
</Section>
</TabPanel>

{/* 3. OnPack Section */}
<TabPanel value={tabValue} index={2}>
<Section title="3. OnPack">
<Box sx={{ m: 'auto', textAlign: 'center' }}>
<Typography variant="body2" color="textSecondary" sx={{ mb: 2 }}>
@@ -226,8 +308,9 @@ const [laserItems, setLaserItems] = useState([
</Button>
</Box>
</Section>
</TabPanel>

{/* 4. Laser Section (HANS600S-M) */}
<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})} />
@@ -283,7 +366,94 @@ const [laserItems, setLaserItems] = useState([
Note: HANS Laser requires pre-saved templates on the controller.
</Typography>
</Section>
</Grid>
</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>

{/* Dialog for OnPack */}
<Dialog open={isPrinterModalOpen} onClose={() => setIsPrinterModalOpen(false)} fullWidth maxWidth="sm">


+ 1
- 1
src/components/CreateItem/CreateItem.tsx Ver fichero

@@ -220,7 +220,7 @@ const CreateItem: React.FC<Props> = ({
variant="scrollable"
>
<Tab label={t("Product / Material Details")} iconPosition="end" />
<Tab label={t("Qc items")} iconPosition="end" />
{/* <Tab label={t("Qc items")} iconPosition="end" /> */}
</Tabs>
{serverError && (
<Typography variant="body2" color="error" alignSelf="flex-end">


+ 15
- 26
src/components/DashboardPage/DashboardPage.tsx Ver fichero

@@ -18,6 +18,7 @@ import CollapsibleCard from "../CollapsibleCard";
import { EscalationResult } from "@/app/api/escalation";
import EscalationLogTable from "./escalation/EscalationLogTable";
import { TruckScheduleDashboard } from "./truckSchedule";
import { GoodsReceiptStatus } from "./goodsReceiptStatus";
type Props = {
// iqc: IQCItems[] | undefined
escalationLogs: EscalationResult[]
@@ -50,20 +51,28 @@ const DashboardPage: React.FC<Props> = ({
</CardContent>
</CollapsibleCard>
</Grid>
<Grid item xs={12}>
<CollapsibleCard title={t("Goods Receipt Status")} defaultOpen={true}>
<CardContent>
<GoodsReceiptStatus />
</CardContent>
</CollapsibleCard>
</Grid>
<Grid item xs={12}>
<CollapsibleCard
title={`${t("Responsible Escalation List")} (${t("pending")} : ${
getPendingLog().length > 0 ? getPendingLog().length : t("No")})`}
showFilter={true}
filterText={t("show completed logs")}
// defaultOpen={getPendingLog().length > 0} // TODO Fix default not opening
>
<CardContent>
<EscalationLogTable items={escLog}/>
</CardContent>
</CollapsibleCard>
</Grid>
<Grid item xs={12}>
{/* Hidden: Progress chart - not in use currently */}
{/* <Grid item xs={12}>
<CollapsibleCard title={t("Progress chart")}>
<CardContent>
<Grid container spacing={3}>
@@ -79,9 +88,10 @@ const DashboardPage: React.FC<Props> = ({
</Grid>
</CardContent>
</CollapsibleCard>
</Grid>
</Grid> */}

<Grid item xs={12}>
{/* Hidden: Warehouse status - not in use currently */}
{/* <Grid item xs={12}>
<CollapsibleCard title={t("Warehouse status")}>
<CardContent>
<Grid container spacing={2}>
@@ -95,31 +105,10 @@ const DashboardPage: React.FC<Props> = ({
</Grid>
</Grid>
</Grid>
{/*<Grid item xs={12} md={6}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<DashboardBox
title={t("Temperature status")}
value="--"
unit="°C"
/>
</Grid>
<Grid item xs={12} sm={6}>
<DashboardBox
title={t("Humidity status")}
value="--"
unit="%"
/>
</Grid>
<Grid item xs={12}>
<DashboardLineChart />
</Grid>
</Grid>
</Grid>*/}
</Grid>
</CardContent>
</CollapsibleCard>
</Grid>
</Grid> */}
</Grid>
</ThemeProvider>
);


+ 163
- 0
src/components/DashboardPage/goodsReceiptStatus/GoodsReceiptStatus.tsx Ver fichero

@@ -0,0 +1,163 @@
"use client";

import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
import {
Box,
Typography,
FormControl,
InputLabel,
Select,
MenuItem,
Card,
CardContent,
Stack,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
CircularProgress,
Chip
} from '@mui/material';
import { useTranslation } from 'react-i18next';
import dayjs from 'dayjs';

interface GoodsReceiptStatusItem {
id: string;
}

const GoodsReceiptStatus: React.FC = () => {
const { t } = useTranslation("dashboard");
const [selectedFilter, setSelectedFilter] = useState<string>("");
const [data, setData] = useState<GoodsReceiptStatusItem[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [currentTime, setCurrentTime] = useState<dayjs.Dayjs | null>(null);
const [isClient, setIsClient] = useState<boolean>(false);
useEffect(() => {
setIsClient(true);
setCurrentTime(dayjs());
}, []);

const loadData = useCallback(async () => {
try {
setData([]);
} catch (error) {
console.error('Error fetching goods receipt status:', error);
} finally {
setLoading(false);
}
}, []);

useEffect(() => {
loadData();
const refreshInterval = setInterval(() => {
loadData();
}, 5 * 60 * 1000);
return () => clearInterval(refreshInterval);
}, [loadData]);

useEffect(() => {
if (!isClient) return;
const timeInterval = setInterval(() => {
setCurrentTime(dayjs());
}, 60 * 1000);
return () => clearInterval(timeInterval);
}, [isClient]);

const filteredData = useMemo(() => {
if (!selectedFilter) return data;
return data.filter(item => true);
}, [data, selectedFilter]);

return (
<Card sx={{ mb: 2 }}>
<CardContent>
{/* Filter */}
<Stack direction="row" spacing={2} sx={{ mb: 3 }}>
<FormControl sx={{ minWidth: 150 }} size="small">
<InputLabel id="filter-select-label" shrink={true}>
{t("Filter")}
</InputLabel>
<Select
labelId="filter-select-label"
id="filter-select"
value={selectedFilter}
label={t("Filter")}
onChange={(e) => setSelectedFilter(e.target.value)}
displayEmpty
>
<MenuItem value="">{t("All")}</MenuItem>
{/* TODO: Add filter options when implementing */}
</Select>
</FormControl>
<Typography variant="body2" sx={{ alignSelf: 'center', color: 'text.secondary' }}>
{t("Auto-refresh every 5 minutes")} | {t("Last updated")}: {isClient && currentTime ? currentTime.format('HH:mm:ss') : '--:--:--'}
</Typography>
</Stack>

{/* Table */}
<Box sx={{ mt: 2 }}>
{loading ? (
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
<CircularProgress />
</Box>
) : (
<TableContainer component={Paper}>
<Table size="small" sx={{ minWidth: 1200 }}>
<TableHead>
<TableRow sx={{ backgroundColor: 'grey.100' }}>
<TableCell sx={{ fontWeight: 600 }}>{t("Column 1")}</TableCell>
<TableCell sx={{ fontWeight: 600 }}>{t("Column 2")}</TableCell>
<TableCell sx={{ fontWeight: 600 }}>{t("Column 3")}</TableCell>
{/* TODO: Add table columns when implementing */}
</TableRow>
</TableHead>
<TableBody>
{filteredData.length === 0 ? (
<TableRow>
<TableCell colSpan={3} align="center">
<Typography variant="body2" color="text.secondary">
{t("No data available")}
</Typography>
</TableCell>
</TableRow>
) : (
filteredData.map((row, index) => (
<TableRow
key={row.id || index}
sx={{
'&:hover': { backgroundColor: 'grey.50' }
}}
>
<TableCell>
{/* TODO: Add table cell content when implementing */}
-
</TableCell>
<TableCell>
-
</TableCell>
<TableCell>
-
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</TableContainer>
)}
</Box>
</CardContent>
</Card>
);
};

export default GoodsReceiptStatus;

+ 1
- 0
src/components/DashboardPage/goodsReceiptStatus/index.ts Ver fichero

@@ -0,0 +1 @@
export { default as GoodsReceiptStatus } from './GoodsReceiptStatus';

+ 0
- 5
src/components/DashboardPage/truckSchedule/TruckScheduleDashboard.tsx Ver fichero

@@ -237,11 +237,6 @@ const TruckScheduleDashboard: React.FC = () => {
return (
<Card sx={{ mb: 2 }}>
<CardContent>
{/* Title */}
<Typography variant="h5" sx={{ mb: 3, fontWeight: 600 }}>
{t("Truck Schedule Dashboard")}
</Typography>

{/* Filter */}
<Stack direction="row" spacing={2} sx={{ mb: 3 }}>
<FormControl sx={{ minWidth: 150 }} size="small">


+ 13
- 26
src/components/ItemsSearch/ItemsSearch.tsx Ver fichero

@@ -7,14 +7,11 @@ import { useTranslation } from "react-i18next";
import SearchResults, { Column } from "../SearchResults";
import { EditNote } from "@mui/icons-material";
import { useRouter, useSearchParams } from "next/navigation";
import { GridDeleteIcon } from "@mui/x-data-grid";
import { Chip } from "@mui/material";
import { TypeEnum } from "@/app/utils/typeEnum";
import axios from "axios";
import { BASE_API_URL, NEXT_PUBLIC_API_URL } from "@/config/api";
import axiosInstance from "@/app/(main)/axios/axiosInstance";
import { deleteItem } from "@/app/api/settings/item/actions";
import { deleteDialog, successDialog } from "../Swal/CustomAlerts";

type Props = {
items: ItemsResult[];
@@ -135,22 +132,6 @@ const ItemsSearch: React.FC<Props> = ({ items }) => {
refetchData,
]);

const onDeleteClick = useCallback(
(item: ItemsResult) => {
deleteDialog(async () => {
if (item.id) {
const itemId = typeof item.id === "string" ? parseInt(item.id, 10) : item.id;
if (!isNaN(itemId)) {
await deleteItem(itemId);
await refetchData(filterObj);
await successDialog(t("Delete Success"), t);
}
}
}, t);
},
[refetchData, filterObj, t],
);

const columns = useMemo<Column<ItemsResultWithStatus>[]>(
() => [
{
@@ -158,22 +139,34 @@ const ItemsSearch: React.FC<Props> = ({ items }) => {
label: t("Details"),
onClick: onDetailClick,
buttonIcon: <EditNote />,
sx: { width: 80 },
},
{
name: "code",
label: t("Code"),
sx: { width: 150 },
},
{
name: "name",
label: t("Name"),
sx: { width: 250 },
},
{
name: "LocationCode",
label: t("LocationCode"),
sx: { width: 150 },
},
{
name: "type",
label: t("Type"),
sx: { width: 120 },
},
{
name: "status",
label: t("Status"),
align: "center",
headerAlign: "center",
sx: { width: 120 },
renderCell: (item) => {
const status = item.status || checkItemStatus(item);
if (status === "complete") {
@@ -183,14 +176,8 @@ const ItemsSearch: React.FC<Props> = ({ items }) => {
}
},
},
{
name: "action",
label: t(""),
buttonIcon: <GridDeleteIcon />,
onClick: onDeleteClick,
},
],
[onDeleteClick, onDetailClick, t, checkItemStatus],
[onDetailClick, t, checkItemStatus],
);

const onReset = useCallback(() => {


+ 1
- 1
src/components/NavigationContent/NavigationContent.tsx Ver fichero

@@ -247,7 +247,7 @@ const NavigationContent: React.FC = () => {
icon: <BugReportIcon />,
label: "PS",
path: "/ps",
requiredAbility: AUTH.TESTING,
requiredAbility: [AUTH.TESTING, AUTH.ADMIN],
isHidden: false,
},
{


+ 15
- 0
src/config/reportConfig.ts Ver fichero

@@ -47,5 +47,20 @@ export const REPORTS: ReportDefinition[] = [
], required: false}
]
},
{
id: "rep-003",
title: "Report 3",
apiEndpoint: `${NEXT_PUBLIC_API_URL}/report/print-report3`,
fields: [
{ label: "From Date", name: "fromDate", type: "date", required: true }, // Mandatory
{ label: "To Date", name: "toDate", type: "date", required: true }, // Mandatory
{ label: "Item Code", name: "itemCode", type: "text", required: false, placeholder: "e.g. FG"},
{ label: "Item Type", name: "itemType", type: "select", required: false,
options: [
{ label: "FG", value: "FG" },
{ label: "Material", value: "Mat" }
] },
]
}
// Add Report 3 to 10 following the same pattern...
];

+ 8
- 1
src/i18n/en/dashboard.json Ver fichero

@@ -72,5 +72,12 @@
"Tickets Completed": "Tickets Completed",
"Last Ticket End": "Last Ticket End",
"Pick Time (min)": "Pick Time (min)",
"No truck schedules available for today": "No truck schedules available for today"
"No truck schedules available for today": "No truck schedules available for today",
"Goods Receipt Status": "Goods Receipt Status",
"Filter": "Filter",
"All": "All",
"Column 1": "Column 1",
"Column 2": "Column 2",
"Column 3": "Column 3",
"No data available": "No data available"
}

+ 8
- 1
src/i18n/zh/dashboard.json Ver fichero

@@ -72,5 +72,12 @@
"Tickets Completed": "已完成成品出倉單",
"Last Ticket End": "末單結束時間",
"Pick Time (min)": "揀貨時間(分鐘)",
"No truck schedules available for today": "今日無車輛調度計劃"
"No truck schedules available for today": "今日無車輛調度計劃",
"Goods Receipt Status": "貨物接收狀態",
"Filter": "篩選",
"All": "全部",
"Column 1": "欄位1",
"Column 2": "欄位2",
"Column 3": "欄位3",
"No data available": "暫無資料"
}

+ 1
- 1
src/i18n/zh/items.json Ver fichero

@@ -33,7 +33,7 @@
"Search": "搜尋",
"Release": "發佈",
"Actions": "操作",
"LocationCode": "位置",
"LocationCode": "預設位置",
"DefaultLocationCode": "預設位置",
"Special Type": "特殊類型",
"None": "無",


Cargando…
Cancelar
Guardar