|
|
@@ -30,7 +30,7 @@ import { |
|
|
} from "@mui/material"; |
|
|
} from "@mui/material"; |
|
|
import AddIcon from "@mui/icons-material/Add"; |
|
|
import AddIcon from "@mui/icons-material/Add"; |
|
|
import SaveIcon from "@mui/icons-material/Save"; |
|
|
import SaveIcon from "@mui/icons-material/Save"; |
|
|
import { useState, useEffect, useMemo } from "react"; |
|
|
|
|
|
|
|
|
import { useState, useMemo } from "react"; |
|
|
import { useRouter } from "next/navigation"; |
|
|
import { useRouter } from "next/navigation"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { findAllUniqueTruckLaneCombinationsClient, createTruckWithoutShopClient } from "@/app/api/shop/client"; |
|
|
import { findAllUniqueTruckLaneCombinationsClient, createTruckWithoutShopClient } from "@/app/api/shop/client"; |
|
|
@@ -50,7 +50,7 @@ const TruckLane: React.FC = () => { |
|
|
const { t } = useTranslation("common"); |
|
|
const { t } = useTranslation("common"); |
|
|
const router = useRouter(); |
|
|
const router = useRouter(); |
|
|
const [truckData, setTruckData] = useState<Truck[]>([]); |
|
|
const [truckData, setTruckData] = useState<Truck[]>([]); |
|
|
const [loading, setLoading] = useState<boolean>(true); |
|
|
|
|
|
|
|
|
const [loading, setLoading] = useState<boolean>(false); |
|
|
const [error, setError] = useState<string | null>(null); |
|
|
const [error, setError] = useState<string | null>(null); |
|
|
const [filters, setFilters] = useState<Record<string, string>>({}); |
|
|
const [filters, setFilters] = useState<Record<string, string>>({}); |
|
|
const [page, setPage] = useState(0); |
|
|
const [page, setPage] = useState(0); |
|
|
@@ -65,32 +65,6 @@ const TruckLane: React.FC = () => { |
|
|
const [snackbarOpen, setSnackbarOpen] = useState<boolean>(false); |
|
|
const [snackbarOpen, setSnackbarOpen] = useState<boolean>(false); |
|
|
const [snackbarMessage, setSnackbarMessage] = useState<string>(""); |
|
|
const [snackbarMessage, setSnackbarMessage] = useState<string>(""); |
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
const fetchTruckLanes = async () => { |
|
|
|
|
|
setLoading(true); |
|
|
|
|
|
setError(null); |
|
|
|
|
|
try { |
|
|
|
|
|
const data = await findAllUniqueTruckLaneCombinationsClient() as Truck[]; |
|
|
|
|
|
// Get unique truckLanceCodes only |
|
|
|
|
|
const uniqueCodes = new Map<string, Truck>(); |
|
|
|
|
|
(data || []).forEach((truck) => { |
|
|
|
|
|
const code = String(truck.truckLanceCode || "").trim(); |
|
|
|
|
|
if (code && !uniqueCodes.has(code)) { |
|
|
|
|
|
uniqueCodes.set(code, truck); |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
setTruckData(Array.from(uniqueCodes.values())); |
|
|
|
|
|
} 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); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
fetchTruckLanes(); |
|
|
|
|
|
}, [t]); |
|
|
|
|
|
|
|
|
|
|
|
// Client-side filtered rows (contains-matching) |
|
|
// Client-side filtered rows (contains-matching) |
|
|
const filteredRows = useMemo(() => { |
|
|
const filteredRows = useMemo(() => { |
|
|
const fKeys = Object.keys(filters).filter((k) => String(filters[k] ?? "").trim() !== ""); |
|
|
const fKeys = Object.keys(filters).filter((k) => String(filters[k] ?? "").trim() !== ""); |
|
|
@@ -125,9 +99,27 @@ const TruckLane: React.FC = () => { |
|
|
return filteredRows.slice(startIndex, startIndex + rowsPerPage); |
|
|
return filteredRows.slice(startIndex, startIndex + rowsPerPage); |
|
|
}, [filteredRows, page, rowsPerPage]); |
|
|
}, [filteredRows, page, rowsPerPage]); |
|
|
|
|
|
|
|
|
const handleSearch = (inputs: Record<string, string>) => { |
|
|
|
|
|
setFilters(inputs); |
|
|
|
|
|
setPage(0); // Reset to first page when searching |
|
|
|
|
|
|
|
|
const handleSearch = async (inputs: Record<string, string>) => { |
|
|
|
|
|
setLoading(true); |
|
|
|
|
|
setError(null); |
|
|
|
|
|
try { |
|
|
|
|
|
const data = await findAllUniqueTruckLaneCombinationsClient() as Truck[]; |
|
|
|
|
|
const uniqueCodes = new Map<string, Truck>(); |
|
|
|
|
|
(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) => { |
|
|
const handlePageChange = (event: unknown, newPage: number) => { |
|
|
@@ -233,24 +225,6 @@ const TruckLane: React.FC = () => { |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
if (loading) { |
|
|
|
|
|
return ( |
|
|
|
|
|
<Box sx={{ display: "flex", justifyContent: "center", p: 4 }}> |
|
|
|
|
|
<CircularProgress /> |
|
|
|
|
|
</Box> |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (error) { |
|
|
|
|
|
return ( |
|
|
|
|
|
<Box> |
|
|
|
|
|
<Alert severity="error" sx={{ mb: 2 }}> |
|
|
|
|
|
{error} |
|
|
|
|
|
</Alert> |
|
|
|
|
|
</Box> |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const criteria: Criterion<SearchParamNames>[] = [ |
|
|
const criteria: Criterion<SearchParamNames>[] = [ |
|
|
{ type: "text", label: t("TruckLance Code"), paramName: "truckLanceCode" }, |
|
|
{ type: "text", label: t("TruckLance Code"), paramName: "truckLanceCode" }, |
|
|
{ type: "time", label: t("Departure Time"), paramName: "departureTime" }, |
|
|
{ type: "time", label: t("Departure Time"), paramName: "departureTime" }, |
|
|
@@ -265,6 +239,7 @@ const TruckLane: React.FC = () => { |
|
|
criteria={criteria as Criterion<string>[]} |
|
|
criteria={criteria as Criterion<string>[]} |
|
|
onSearch={handleSearch} |
|
|
onSearch={handleSearch} |
|
|
onReset={() => { |
|
|
onReset={() => { |
|
|
|
|
|
setTruckData([]); |
|
|
setFilters({}); |
|
|
setFilters({}); |
|
|
}} |
|
|
}} |
|
|
/> |
|
|
/> |
|
|
@@ -284,7 +259,17 @@ const TruckLane: React.FC = () => { |
|
|
{t("Add Truck Lane")} |
|
|
{t("Add Truck Lane")} |
|
|
</Button> |
|
|
</Button> |
|
|
</Box> |
|
|
</Box> |
|
|
|
|
|
|
|
|
|
|
|
{error && ( |
|
|
|
|
|
<Alert severity="error" sx={{ mb: 2 }}> |
|
|
|
|
|
{error} |
|
|
|
|
|
</Alert> |
|
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
|
|
{loading ? ( |
|
|
|
|
|
<Box sx={{ display: "flex", justifyContent: "center", p: 4 }}> |
|
|
|
|
|
<CircularProgress /> |
|
|
|
|
|
</Box> |
|
|
|
|
|
) : ( |
|
|
<TableContainer component={Paper}> |
|
|
<TableContainer component={Paper}> |
|
|
<Table> |
|
|
<Table> |
|
|
<TableHead> |
|
|
<TableHead> |
|
|
@@ -356,6 +341,7 @@ const TruckLane: React.FC = () => { |
|
|
rowsPerPageOptions={[5, 10, 25, 50]} |
|
|
rowsPerPageOptions={[5, 10, 25, 50]} |
|
|
/> |
|
|
/> |
|
|
</TableContainer> |
|
|
</TableContainer> |
|
|
|
|
|
)} |
|
|
</CardContent> |
|
|
</CardContent> |
|
|
</Card> |
|
|
</Card> |
|
|
|
|
|
|
|
|
|