Ver a proveniência

Update Shop Detail page, add remark column

master
Tommy\2Fi-Staff há 12 horas
ascendente
cometimento
af805a8c0d
2 ficheiros alterados com 201 adições e 24 eliminações
  1. +6
    -2
      src/app/api/shop/actions.ts
  2. +195
    -22
      src/components/Shop/ShopDetail.tsx

+ 6
- 2
src/app/api/shop/actions.ts Ver ficheiro

@@ -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 {


+ 195
- 22
src/components/Shop/ShopDetail.tsx Ver ficheiro

@@ -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<string[]>([]);
const [snackbarOpen, setSnackbarOpen] = useState<boolean>(false);
const [snackbarMessage, setSnackbarMessage] = useState<string>("");

@@ -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 = () => {
<TableCell>Loading Sequence</TableCell>
<TableCell>District Reference</TableCell>
<TableCell>Store ID</TableCell>
<TableCell>Remark</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody>
{truckData.length === 0 ? (
<TableRow>
<TableCell colSpan={6} align="center">
<TableCell colSpan={7} align="center">
<Typography variant="body2" color="text.secondary">
No Truck data available
</Typography>
@@ -587,15 +659,81 @@ const ShopDetail: React.FC = () => {
</TableCell>
<TableCell>
{isEditing ? (
<TextField
size="small"
type="number"
value={displayTruck?.storeId ?? 0}
onChange={(e) => handleTruckFieldChange(index, "storeId", parseInt(e.target.value) || 0)}
fullWidth
/>
<FormControl size="small" fullWidth>
<Select
value={(() => {
const storeId = displayTruck?.storeId;
if (!storeId) return "2F";
const storeIdStr = typeof storeId === 'string' ? storeId : String(storeId);
// Convert numeric values to string format
if (storeIdStr === "2" || storeIdStr === "2F") return "2F";
if (storeIdStr === "4" || storeIdStr === "4F") return "4F";
return storeIdStr;
})()}
onChange={(e) => {
const newStoreId = e.target.value;
handleTruckFieldChange(index, "storeId", newStoreId);
// Clear remark if storeId changes from 4F to something else
if (newStoreId !== "4F") {
handleTruckFieldChange(index, "remark", "");
}
}}
>
<MenuItem value="2F">2F</MenuItem>
<MenuItem value="4F">4F</MenuItem>
</Select>
</FormControl>
) : (
(() => {
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;
})()
)}
</TableCell>
<TableCell>
{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 (
<Autocomplete
freeSolo
size="small"
disabled={!isEditable}
options={uniqueRemarks}
value={displayTruck?.remark ? String(displayTruck.remark) : ""}
onChange={(event, newValue) => {
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) => (
<TextField
{...params}
fullWidth
placeholder={isEditable ? "Enter or select remark" : "Not editable for this Store ID"}
disabled={!isEditable}
/>
)}
/>
);
})()
) : (
truck.storeId !== null && truck.storeId !== undefined ? String(truck.storeId) : "-"
String(truck.remark || "-")
)}
</TableCell>
<TableCell>
@@ -711,15 +849,50 @@ const ShopDetail: React.FC = () => {
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Store ID"
type="number"
fullWidth
value={newTruck.storeId}
onChange={(e) => setNewTruck({ ...newTruck, storeId: parseInt(e.target.value) || 2 })}
disabled={saving}
/>
<FormControl fullWidth>
<InputLabel>Store ID</InputLabel>
<Select
value={newTruck.storeId}
label="Store ID"
onChange={(e) => {
const newStoreId = e.target.value;
setNewTruck({
...newTruck,
storeId: newStoreId,
remark: newStoreId === "4F" ? newTruck.remark : ""
});
}}
disabled={saving}
>
<MenuItem value="2F">2F</MenuItem>
<MenuItem value="4F">4F</MenuItem>
</Select>
</FormControl>
</Grid>
{newTruck.storeId === "4F" && (
<Grid item xs={12}>
<Autocomplete
freeSolo
options={uniqueRemarks}
value={newTruck.remark || ""}
onChange={(event, newValue) => {
setNewTruck({ ...newTruck, remark: newValue || "" });
}}
onInputChange={(event, newInputValue) => {
setNewTruck({ ...newTruck, remark: newInputValue });
}}
renderInput={(params) => (
<TextField
{...params}
label="Remark"
fullWidth
placeholder="Enter or select remark"
disabled={saving}
/>
)}
/>
</Grid>
)}
</Grid>
</Box>
</DialogContent>


Carregando…
Cancelar
Guardar