"use client"; import { Box, Card, CardContent, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TablePagination, TableRow, Paper, Button, CircularProgress, Alert, Dialog, DialogTitle, DialogContent, DialogActions, TextField, Grid, FormControl, InputLabel, Select, MenuItem, Snackbar, } from "@mui/material"; import AddIcon from "@mui/icons-material/Add"; import SaveIcon from "@mui/icons-material/Save"; import { useState, useMemo } from "react"; import { useRouter } from "next/navigation"; import { useTranslation } from "react-i18next"; import { findAllUniqueTruckLaneCombinationsClient, createTruckWithoutShopClient } from "@/app/api/shop/client"; import type { Truck } from "@/app/api/shop/actions"; import SearchBox, { Criterion } from "../SearchBox"; import { formatDepartureTime, normalizeStoreId } from "@/app/utils/formatUtil"; type SearchQuery = { truckLanceCode: string; departureTime: string; storeId: string; }; type SearchParamNames = keyof SearchQuery; const TruckLane: React.FC = () => { const { t } = useTranslation("common"); const router = useRouter(); const [truckData, setTruckData] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [filters, setFilters] = useState>({}); const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(10); const [addDialogOpen, setAddDialogOpen] = useState(false); const [newTruck, setNewTruck] = useState({ truckLanceCode: "", departureTime: "", storeId: "2F", }); const [saving, setSaving] = useState(false); const [snackbarOpen, setSnackbarOpen] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState(""); // Client-side filtered rows (contains-matching) const filteredRows = useMemo(() => { const fKeys = Object.keys(filters).filter((k) => String(filters[k] ?? "").trim() !== ""); if (fKeys.length === 0) return truckData; return truckData.filter((truck) => { for (const key of fKeys) { const filterValue = String(filters[key] ?? "").trim().toLowerCase(); if (key === "truckLanceCode") { const truckCode = String(truck.truckLanceCode ?? "").trim().toLowerCase(); if (!truckCode.includes(filterValue)) return false; } else if (key === "departureTime") { const formattedTime = formatDepartureTime( Array.isArray(truck.departureTime) ? truck.departureTime : (truck.departureTime ? String(truck.departureTime) : null) ); if (!formattedTime.toLowerCase().includes(filterValue)) return false; } else if (key === "storeId") { const displayStoreId = normalizeStoreId(truck.storeId); if (!displayStoreId.toLowerCase().includes(filterValue)) return false; } } return true; }); }, [truckData, filters]); // Paginated rows const paginatedRows = useMemo(() => { const startIndex = page * rowsPerPage; return filteredRows.slice(startIndex, startIndex + rowsPerPage); }, [filteredRows, page, rowsPerPage]); const handleSearch = async (inputs: Record) => { setLoading(true); setError(null); try { const data = await findAllUniqueTruckLaneCombinationsClient() as Truck[]; const uniqueCodes = new Map(); (data || []).forEach((truck) => { const code = String(truck.truckLanceCode ?? "").trim(); if (code && !uniqueCodes.has(code)) { uniqueCodes.set(code, truck); } }); setTruckData(Array.from(uniqueCodes.values())); setFilters(inputs); setPage(0); } catch (err: any) { console.error("Failed to load truck lanes:", err); setError(err?.message ?? String(err) ?? t("Failed to load truck lanes")); } finally { setLoading(false); } }; const handlePageChange = (event: unknown, newPage: number) => { setPage(newPage); }; const handleRowsPerPageChange = (event: React.ChangeEvent) => { setRowsPerPage(parseInt(event.target.value, 10)); setPage(0); // Reset to first page when changing rows per page }; const handleViewDetail = (truck: Truck) => { // Navigate to truck lane detail page using truckLanceCode const truckLanceCode = String(truck.truckLanceCode || "").trim(); if (truckLanceCode) { // Use router.push with proper URL encoding const url = new URL(`/settings/shop/truckdetail`, window.location.origin); url.searchParams.set("truckLanceCode", truckLanceCode); router.push(url.pathname + url.search); } }; const handleOpenAddDialog = () => { setNewTruck({ truckLanceCode: "", departureTime: "", storeId: "2F", }); setAddDialogOpen(true); setError(null); }; const handleCloseAddDialog = () => { setAddDialogOpen(false); setNewTruck({ truckLanceCode: "", departureTime: "", storeId: "2F", }); }; const handleCreateTruck = async () => { // Validate all required fields const missingFields: string[] = []; if (!newTruck.truckLanceCode.trim()) { missingFields.push(t("TruckLance Code")); } if (!newTruck.departureTime) { missingFields.push(t("Departure Time")); } if (missingFields.length > 0) { const message = `${t("Please fill in the following required fields:")} ${missingFields.join(", ")}`; setSnackbarMessage(message); setSnackbarOpen(true); return; } // Check if truckLanceCode already exists const trimmedCode = newTruck.truckLanceCode.trim(); const existingTruck = truckData.find( (truck) => String(truck.truckLanceCode || "").trim().toLowerCase() === trimmedCode.toLowerCase() ); if (existingTruck) { setSnackbarMessage(t("Truck lane code already exists. Please use a different code.")); setSnackbarOpen(true); return; } setSaving(true); setError(null); try { await createTruckWithoutShopClient({ store_id: newTruck.storeId, truckLanceCode: newTruck.truckLanceCode.trim(), departureTime: newTruck.departureTime.trim(), loadingSequence: 0, districtReference: null, remark: null, }); // Refresh truck data after create const data = await findAllUniqueTruckLaneCombinationsClient() as Truck[]; const uniqueCodes = new Map(); data.forEach((truck) => { const code = String(truck.truckLanceCode ?? "").trim(); if (code && !uniqueCodes.has(code)) { uniqueCodes.set(code, truck); } }); setTruckData(Array.from(uniqueCodes.values())); handleCloseAddDialog(); } catch (err: unknown) { console.error("Failed to create truck:", err); const errorMessage = err instanceof Error ? err.message : String(err); setError(errorMessage || t("Failed to create truck")); } finally { setSaving(false); } }; const criteria: Criterion[] = [ { type: "text", label: t("TruckLance Code"), paramName: "truckLanceCode" }, { type: "time", label: t("Departure Time"), paramName: "departureTime" }, { type: "text", label: t("Store ID"), paramName: "storeId" }, ]; return ( []} onSearch={handleSearch} onReset={() => { setTruckData([]); setFilters({}); }} /> {t("Truck Lane")} {error && ( {error} )} {loading ? ( ) : ( {t("TruckLance Code")} {t("Departure Time")} {t("Store ID")} {t("Actions")} {paginatedRows.length === 0 ? ( {t("No Truck Lane data available")} ) : ( paginatedRows.map((truck) => ( {String(truck.truckLanceCode ?? "-")} {formatDepartureTime( Array.isArray(truck.departureTime) ? truck.departureTime : (truck.departureTime ? String(truck.departureTime) : null) )} {normalizeStoreId( truck.storeId ? (typeof truck.storeId === 'string' || truck.storeId instanceof String ? String(truck.storeId) : String(truck.storeId)) : null )} )) )}
)}
{/* Add Truck Dialog */} {t("Add New Truck Lane")} setNewTruck({ ...newTruck, truckLanceCode: e.target.value })} disabled={saving} /> setNewTruck({ ...newTruck, departureTime: e.target.value })} disabled={saving} InputLabelProps={{ shrink: true, }} inputProps={{ step: 300, // 5 minutes }} /> {t("Store ID")} {/* Snackbar for notifications */} setSnackbarOpen(false)} anchorOrigin={{ vertical: 'top', horizontal: 'center' }} > setSnackbarOpen(false)} severity="warning" sx={{ width: '100%' }} > {snackbarMessage}
); }; export default TruckLane;