From af805a8c0d8a8feb87da020abfce15a40a57cf5b Mon Sep 17 00:00:00 2001 From: "Tommy\\2Fi-Staff" Date: Fri, 2 Jan 2026 17:25:03 +0800 Subject: [PATCH] Update Shop Detail page, add remark column --- src/app/api/shop/actions.ts | 8 +- src/components/Shop/ShopDetail.tsx | 217 ++++++++++++++++++++++++++--- 2 files changed, 201 insertions(+), 24 deletions(-) diff --git a/src/app/api/shop/actions.ts b/src/app/api/shop/actions.ts index 8479ddd..559a269 100644 --- a/src/app/api/shop/actions.ts +++ b/src/app/api/shop/actions.ts @@ -42,7 +42,8 @@ export interface Truck{ departureTime: String | number[]; loadingSequence: number; districtReference: Number; - storeId: Number; + storeId: Number | String; + remark?: String | null; } export interface SaveTruckLane { @@ -51,6 +52,8 @@ export interface SaveTruckLane { departureTime: string; loadingSequence: number; districtReference: number; + storeId: string; + remark?: string | null; } export interface DeleteTruckLane { @@ -59,7 +62,7 @@ export interface DeleteTruckLane { export interface SaveTruckRequest { id?: number | null; - store_id: number; + store_id: string; truckLanceCode: string; departureTime: string; shopId: number; @@ -67,6 +70,7 @@ export interface SaveTruckRequest { shopCode: string; loadingSequence: number; districtReference?: number | null; + remark?: string | null; } export interface MessageResponse { diff --git a/src/components/Shop/ShopDetail.tsx b/src/components/Shop/ShopDetail.tsx index 2518afb..f2e4282 100644 --- a/src/components/Shop/ShopDetail.tsx +++ b/src/components/Shop/ShopDetail.tsx @@ -24,6 +24,11 @@ import { DialogActions, Grid, Snackbar, + Select, + MenuItem, + FormControl, + InputLabel, + Autocomplete, } from "@mui/material"; import DeleteIcon from "@mui/icons-material/Delete"; import EditIcon from "@mui/icons-material/Edit"; @@ -143,8 +148,10 @@ const ShopDetail: React.FC = () => { departureTime: "", loadingSequence: 0, districtReference: 0, - storeId: 2, + storeId: "2F", + remark: "", }); + const [uniqueRemarks, setUniqueRemarks] = useState([]); const [snackbarOpen, setSnackbarOpen] = useState(false); const [snackbarMessage, setSnackbarMessage] = useState(""); @@ -215,6 +222,14 @@ const ShopDetail: React.FC = () => { setTruckData(trucks || []); setEditedTruckData(trucks || []); setEditingRowIndex(null); + + // Extract unique remarks from trucks for this shop + const remarks = trucks + ?.map(t => t.remark) + .filter((remark): remark is string => remark != null && String(remark).trim() !== "") + .map(r => String(r).trim()) + .filter((value, index, self) => self.indexOf(value) === index) || []; + setUniqueRemarks(remarks); } catch (err: any) { console.error("Failed to load shop detail:", err); // Handle errors gracefully - don't trigger auto-logout @@ -232,6 +247,20 @@ const ShopDetail: React.FC = () => { setEditingRowIndex(index); const updated = [...truckData]; updated[index] = { ...updated[index] }; + // Normalize departureTime to HH:mm format for editing + if (updated[index].departureTime) { + const timeValue = updated[index].departureTime; + const formatted = formatDepartureTime( + Array.isArray(timeValue) ? timeValue : (timeValue ? String(timeValue) : null) + ); + if (formatted !== "-") { + updated[index].departureTime = formatted; + } + } + // Ensure remark is initialized as string (not null/undefined) + if (updated[index].remark == null) { + updated[index].remark = ""; + } setEditedTruckData(updated); setError(null); }; @@ -257,8 +286,28 @@ const ShopDetail: React.FC = () => { setSaving(true); setError(null); try { - // Convert departureTime to proper format if needed - const departureTime = parseDepartureTimeForBackend(String(truck.departureTime || "")); + // Use the departureTime from editedTruckData which is already in HH:mm format from the input field + // If it's already a valid HH:mm string, use it directly; otherwise format it + let departureTime = String(truck.departureTime || "").trim(); + if (!departureTime || departureTime === "-") { + departureTime = ""; + } else if (!/^\d{1,2}:\d{2}$/.test(departureTime)) { + // Only convert if it's not already in HH:mm format + departureTime = parseDepartureTimeForBackend(departureTime); + } + + // Convert storeId to string format (2F or 4F) + const storeIdStr = truck.storeId ? (typeof truck.storeId === 'string' ? truck.storeId : String(truck.storeId) === "2" ? "2F" : String(truck.storeId) === "4" ? "4F" : String(truck.storeId)) : "2F"; + + // Get remark value - use the remark from editedTruckData (user input) + // Only send remark if storeId is "4F", otherwise send null + let remarkValue: string | null = null; + if (storeIdStr === "4F") { + const remark = truck.remark; + if (remark != null && String(remark).trim() !== "") { + remarkValue = String(remark).trim(); + } + } await updateTruckLaneClient({ id: truck.id, @@ -266,6 +315,8 @@ const ShopDetail: React.FC = () => { departureTime: departureTime, loadingSequence: Number(truck.loadingSequence) || 0, districtReference: Number(truck.districtReference) || 0, + storeId: storeIdStr, + remark: remarkValue, }); // Refresh truck data after update @@ -274,6 +325,14 @@ const ShopDetail: React.FC = () => { setTruckData(trucks || []); setEditedTruckData(trucks || []); setEditingRowIndex(null); + + // Update unique remarks + const remarks = trucks + ?.map(t => t.remark) + .filter((remark): remark is string => remark != null && String(remark).trim() !== "") + .map(r => String(r).trim()) + .filter((value, index, self) => self.indexOf(value) === index) || []; + setUniqueRemarks(remarks); } catch (err: any) { console.error("Failed to save truck data:", err); setError(err?.message ?? String(err) ?? "Failed to save truck data"); @@ -326,7 +385,8 @@ const ShopDetail: React.FC = () => { departureTime: "", loadingSequence: 0, districtReference: 0, - storeId: 2, + storeId: "2F", + remark: "", }); setAddDialogOpen(true); setError(null); @@ -339,7 +399,8 @@ const ShopDetail: React.FC = () => { departureTime: "", loadingSequence: 0, districtReference: 0, - storeId: 2, + storeId: "2F", + remark: "", }); }; @@ -380,6 +441,7 @@ const ShopDetail: React.FC = () => { shopCode: String(shopDetail!.code), loadingSequence: newTruck.loadingSequence, districtReference: newTruck.districtReference, + remark: newTruck.storeId === "4F" ? (newTruck.remark?.trim() || null) : null, }); // Refresh truck data after create @@ -387,6 +449,15 @@ const ShopDetail: React.FC = () => { const trucks = await findTruckLaneByShopIdClient(shopIdNum) as Truck[]; setTruckData(trucks || []); setEditedTruckData(trucks || []); + + // Update unique remarks + const remarks = trucks + ?.map(t => t.remark) + .filter((remark): remark is string => remark != null && String(remark).trim() !== "") + .map(r => String(r).trim()) + .filter((value, index, self) => self.indexOf(value) === index) || []; + setUniqueRemarks(remarks); + handleCloseAddDialog(); } catch (err: any) { console.error("Failed to create truck:", err); @@ -501,13 +572,14 @@ const ShopDetail: React.FC = () => { Loading Sequence District Reference Store ID + Remark Actions {truckData.length === 0 ? ( - + No Truck data available @@ -587,15 +659,81 @@ const ShopDetail: React.FC = () => { {isEditing ? ( - handleTruckFieldChange(index, "storeId", parseInt(e.target.value) || 0)} - fullWidth - /> + + + + ) : ( + (() => { + const storeId = truck.storeId; + if (storeId === null || storeId === undefined) return "-"; + const storeIdStr = typeof storeId === 'string' ? storeId : String(storeId); + // Convert numeric values to display format + if (storeIdStr === "2" || storeIdStr === "2F") return "2F"; + if (storeIdStr === "4" || storeIdStr === "4F") return "4F"; + return storeIdStr; + })() + )} + + + {isEditing ? ( + (() => { + const storeId = displayTruck?.storeId; + const storeIdStr = storeId ? (typeof storeId === 'string' ? storeId : String(storeId) === "2" ? "2F" : String(storeId) === "4" ? "4F" : String(storeId)) : "2F"; + const isEditable = storeIdStr === "4F"; + + return ( + { + if (isEditable) { + const remarkValue = typeof newValue === 'string' ? newValue : (newValue || ""); + handleTruckFieldChange(index, "remark", remarkValue); + } + }} + onInputChange={(event, newInputValue, reason) => { + // Only update on user input, not when clearing or selecting + if (isEditable && (reason === 'input' || reason === 'clear')) { + handleTruckFieldChange(index, "remark", newInputValue); + } + }} + renderInput={(params) => ( + + )} + /> + ); + })() ) : ( - truck.storeId !== null && truck.storeId !== undefined ? String(truck.storeId) : "-" + String(truck.remark || "-") )} @@ -711,15 +849,50 @@ const ShopDetail: React.FC = () => { /> - setNewTruck({ ...newTruck, storeId: parseInt(e.target.value) || 2 })} - disabled={saving} - /> + + Store ID + + + {newTruck.storeId === "4F" && ( + + { + setNewTruck({ ...newTruck, remark: newValue || "" }); + }} + onInputChange={(event, newInputValue) => { + setNewTruck({ ...newTruck, remark: newInputValue }); + }} + renderInput={(params) => ( + + )} + /> + + )}