Explorar el Código

update shop and truck

MergeProblem1
Tommy\2Fi-Staff hace 2 días
padre
commit
1f56e1b5bd
Se han modificado 5 ficheros con 577 adiciones y 778 borrados
  1. +1
    -6
      src/components/Shop/Shop.tsx
  2. +153
    -444
      src/components/Shop/ShopDetail.tsx
  3. +397
    -327
      src/components/Shop/TruckLaneDetail.tsx
  4. +16
    -1
      src/i18n/en/common.json
  5. +10
    -0
      src/i18n/zh/common.json

+ 1
- 6
src/components/Shop/Shop.tsx Ver fichero

@@ -134,11 +134,6 @@ const Shop: React.FC = () => {
const loadingSeqNum = loadingSeq != null && loadingSeq !== undefined ? Number(loadingSeq) : null;
const hasLoadingSequence = loadingSeqNum !== null && !isNaN(loadingSeqNum) && loadingSeqNum !== 0;
// Check districtReference: must exist and not be 0
const districtRef = (truck as any).districtReference;
const districtRefNum = districtRef != null && districtRef !== undefined ? Number(districtRef) : null;
const hasDistrictReference = districtRefNum !== null && !isNaN(districtRefNum) && districtRefNum !== 0;
// Check storeId: must exist and not be 0 (can be string "2F"/"4F" or number)
// Actual field name in JSON is store_id (underscore, lowercase)
const storeId = (truck as any).store_id || (truck as any).storeId || (truck as any).Store_id;
@@ -158,7 +153,7 @@ const Shop: React.FC = () => {
}

// If any required field is missing or equals 0, return "missing"
if (!hasTruckLanceCode || !hasDepartureTime || !hasLoadingSequence || !hasDistrictReference || !storeIdValid) {
if (!hasTruckLanceCode || !hasDepartureTime || !hasLoadingSequence || !storeIdValid) {
return "missing";
}
}


+ 153
- 444
src/components/Shop/ShopDetail.tsx Ver fichero

@@ -24,28 +24,24 @@ 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";
import SaveIcon from "@mui/icons-material/Save";
import CancelIcon from "@mui/icons-material/Cancel";
import AddIcon from "@mui/icons-material/Add";
import CheckIcon from "@mui/icons-material/Check";
import CloseIcon from "@mui/icons-material/Close";
import { useRouter, useSearchParams } from "next/navigation";
import { useState, useEffect } from "react";
import { useSession } from "next-auth/react";
import { useTranslation } from "react-i18next";
import type { Shop, ShopAndTruck, Truck } from "@/app/api/shop/actions";
import type { ShopAndTruck, Truck } from "@/app/api/shop/actions";
import {
fetchAllShopsClient,
findTruckLaneByShopIdClient,
updateTruckLaneClient,
deleteTruckLaneClient,
createTruckClient
createTruckClient,
findAllUniqueTruckLaneCombinationsClient,
} from "@/app/api/shop/client";
import type { SessionWithTokens } from "@/config/authConfig";
import { formatDepartureTime, normalizeStoreId } from "@/app/utils/formatUtil";
@@ -77,6 +73,32 @@ const parseDepartureTimeForBackend = (time: string): string => {
return formatDepartureTime(timeStr);
};

/** Label for truck lane picker: code + optional remark (unique combo from API). */
const getTruckLaneOptionLabel = (lane: Truck): string => {
const code = String(lane.truckLanceCode ?? "").trim();
const remark =
lane.remark != null && String(lane.remark).trim() !== "" ? String(lane.remark).trim() : null;
return remark ? `${code} — ${remark}` : code || "-";
};

const isSameTruckLaneOption = (a: Truck | null, b: Truck | null): boolean => {
if (a === b) return true;
if (!a || !b) return false;
const sameCode = String(a.truckLanceCode ?? "") === String(b.truckLanceCode ?? "");
const ra = a.remark != null ? String(a.remark) : "";
const rb = b.remark != null ? String(b.remark) : "";
return sameCode && ra === rb;
};

/** Build HH:mm string from API departureTime for backend parsing. */
const departureTimeToStringForSave = (timeValue: Truck["departureTime"]): string => {
const formatted = formatDepartureTime(
Array.isArray(timeValue) ? timeValue : timeValue ? String(timeValue) : null
);
if (!formatted || formatted === "-") return "";
return parseDepartureTimeForBackend(formatted);
};

const ShopDetail: React.FC = () => {
const { t } = useTranslation("common");
const router = useRouter();
@@ -85,21 +107,15 @@ const ShopDetail: React.FC = () => {
const { data: session, status: sessionStatus } = useSession() as { data: SessionWithTokens | null; status: string };
const [shopDetail, setShopDetail] = useState<ShopDetailData | null>(null);
const [truckData, setTruckData] = useState<Truck[]>([]);
const [editedTruckData, setEditedTruckData] = useState<Truck[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const [editingRowIndex, setEditingRowIndex] = useState<number | null>(null);
const [saving, setSaving] = useState<boolean>(false);
const [confirmingDeleteIndex, setConfirmingDeleteIndex] = useState<number | null>(null);
const [addDialogOpen, setAddDialogOpen] = useState<boolean>(false);
const [newTruck, setNewTruck] = useState({
truckLanceCode: "",
departureTime: "",
loadingSequence: 0,
districtReference: 0,
storeId: "2F",
remark: "",
});
const [uniqueRemarks, setUniqueRemarks] = useState<string[]>([]);
const [availableTruckLanes, setAvailableTruckLanes] = useState<Truck[]>([]);
const [selectedTruckLane, setSelectedTruckLane] = useState<Truck | null>(null);
const [addLoadingSequence, setAddLoadingSequence] = useState<number>(0);
const [loadingTruckLanes, setLoadingTruckLanes] = useState<boolean>(false);
const [snackbarOpen, setSnackbarOpen] = useState<boolean>(false);
const [snackbarMessage, setSnackbarMessage] = useState<string>("");

@@ -168,16 +184,6 @@ const ShopDetail: React.FC = () => {
// Fetch truck information using the Truck interface with numeric ID
const trucks = await findTruckLaneByShopIdClient(shopIdNum) as Truck[];
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
@@ -191,118 +197,7 @@ const ShopDetail: React.FC = () => {
fetchShopDetail();
}, [shopId, sessionStatus, session]);

const handleEdit = (index: number) => {
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);
};

const handleCancel = (index: number) => {
setEditingRowIndex(null);
setEditedTruckData([...truckData]);
setError(null);
};

const handleSave = async (index: number) => {
if (!shopId) {
setError(t("Shop ID is required"));
return;
}

const truck = editedTruckData[index];
if (!truck || !truck.id) {
setError(t("Invalid shop data"));
return;
}

setSaving(true);
setError(null);
try {
// 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 = normalizeStoreId(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,
truckLanceCode: String(truck.truckLanceCode || ""),
departureTime: departureTime,
loadingSequence: Number(truck.loadingSequence) || 0,
districtReference: Number(truck.districtReference) || 0,
storeId: storeIdStr,
remark: remarkValue,
});
// Refresh truck data after update
const shopIdNum = parseInt(shopId, 10);
const trucks = await findTruckLaneByShopIdClient(shopIdNum) as Truck[];
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) ?? t("Failed to save truck data"));
} finally {
setSaving(false);
}
};

const handleTruckFieldChange = (index: number, field: keyof Truck, value: string | number) => {
const updated = [...editedTruckData];
updated[index] = {
...updated[index],
[field]: value,
};
setEditedTruckData(updated);
};

const handleDelete = async (truckId: number) => {
if (!window.confirm(t("Are you sure you want to delete this truck lane?"))) {
return;
}

if (!shopId) {
setError(t("Shop ID is required"));
return;
@@ -312,13 +207,11 @@ const ShopDetail: React.FC = () => {
setError(null);
try {
await deleteTruckLaneClient({ id: truckId });
// Refresh truck data after delete
setConfirmingDeleteIndex(null);
const shopIdNum = parseInt(shopId, 10);
const trucks = await findTruckLaneByShopIdClient(shopIdNum) as Truck[];
setTruckData(trucks || []);
setEditedTruckData(trucks || []);
setEditingRowIndex(null);
} catch (err: any) {
console.error("Failed to delete truck lane:", err);
setError(err?.message ?? String(err) ?? t("Failed to delete truck lane"));
@@ -327,44 +220,49 @@ const ShopDetail: React.FC = () => {
}
};

const handleOpenAddDialog = () => {
setNewTruck({
truckLanceCode: "",
departureTime: "",
loadingSequence: 0,
districtReference: 0,
storeId: "2F",
remark: "",
});
const handleOpenAddDialog = async () => {
setSelectedTruckLane(null);
setAddLoadingSequence(0);
setAddDialogOpen(true);
setError(null);
setLoadingTruckLanes(true);
try {
const lanes = (await findAllUniqueTruckLaneCombinationsClient()) as Truck[];
setAvailableTruckLanes(lanes || []);
} catch (err: any) {
console.error("Failed to load truck lanes:", err);
setAvailableTruckLanes([]);
setSnackbarMessage(
err?.message ?? String(err) ?? t("Failed to load truck lanes")
);
setSnackbarOpen(true);
} finally {
setLoadingTruckLanes(false);
}
};

const handleCloseAddDialog = () => {
setAddDialogOpen(false);
setNewTruck({
truckLanceCode: "",
departureTime: "",
loadingSequence: 0,
districtReference: 0,
storeId: "2F",
remark: "",
});
setSelectedTruckLane(null);
setAddLoadingSequence(0);
setAvailableTruckLanes([]);
};

const handleCreateTruck = async () => {
// Validate all required fields
const missingFields: string[] = [];

if (!shopId || !shopDetail) {
missingFields.push(t("Shop Information"));
}

if (!newTruck.truckLanceCode.trim()) {
if (!selectedTruckLane) {
missingFields.push(t("TruckLance Code"));
}

if (!newTruck.departureTime) {
const departureTime = selectedTruckLane
? departureTimeToStringForSave(selectedTruckLane.departureTime)
: "";
if (!departureTime) {
missingFields.push(t("Departure Time"));
}

@@ -375,36 +273,32 @@ const ShopDetail: React.FC = () => {
return;
}

const lane = selectedTruckLane!;
const storeIdStr = normalizeStoreId(lane.storeId) || "2F";
const remarkValue =
storeIdStr === "4F" && lane.remark != null && String(lane.remark).trim() !== ""
? String(lane.remark).trim()
: null;

setSaving(true);
setError(null);
try {
const departureTime = parseDepartureTimeForBackend(newTruck.departureTime);
await createTruckClient({
store_id: newTruck.storeId,
truckLanceCode: newTruck.truckLanceCode.trim(),
store_id: storeIdStr,
truckLanceCode: String(lane.truckLanceCode || "").trim(),
departureTime: departureTime,
shopId: shopDetail!.id,
shopName: String(shopDetail!.name),
shopCode: String(shopDetail!.code),
loadingSequence: newTruck.loadingSequence,
districtReference: newTruck.districtReference,
remark: newTruck.storeId === "4F" ? (newTruck.remark?.trim() || null) : null,
loadingSequence: addLoadingSequence,
districtReference: Number(lane.districtReference) || 0,
remark: remarkValue,
});
// Refresh truck data after create
const shopIdNum = parseInt(shopId || "0", 10);
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) {
@@ -502,7 +396,7 @@ const ShopDetail: React.FC = () => {
variant="contained"
startIcon={<AddIcon />}
onClick={handleOpenAddDialog}
disabled={editingRowIndex !== null || saving}
disabled={saving}
>
{t("Add Truck Lane")}
</Button>
@@ -517,7 +411,7 @@ const ShopDetail: React.FC = () => {
<TableCell>{t("District Reference")}</TableCell>
<TableCell>{t("Store ID")}</TableCell>
<TableCell>{t("Remark")}</TableCell>
<TableCell>{t("Actions")}</TableCell>
<TableCell align="right">{t("Actions")}</TableCell>
</TableRow>
</TableHead>
<TableBody>
@@ -530,199 +424,70 @@ const ShopDetail: React.FC = () => {
</TableCell>
</TableRow>
) : (
truckData.map((truck, index) => {
const isEditing = editingRowIndex === index;
const displayTruck = isEditing ? editedTruckData[index] : truck;
return (
<TableRow key={truck.id ?? `truck-${index}`}>
<TableCell>
{isEditing ? (
<TextField
size="small"
value={String(displayTruck?.truckLanceCode || "")}
onChange={(e) => handleTruckFieldChange(index, "truckLanceCode", e.target.value)}
fullWidth
/>
) : (
String(truck.truckLanceCode || "-")
)}
</TableCell>
<TableCell>
{isEditing ? (
<TextField
size="small"
type="time"
value={(() => {
const timeValue = displayTruck?.departureTime;
const formatted = formatDepartureTime(
Array.isArray(timeValue) ? timeValue : (timeValue ? String(timeValue) : null)
);
return formatted !== "-" ? formatted : "";
})()}
onChange={(e) => handleTruckFieldChange(index, "departureTime", e.target.value)}
fullWidth
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 minutes
}}
/>
) : (
formatDepartureTime(
Array.isArray(truck.departureTime) ? truck.departureTime : (truck.departureTime ? String(truck.departureTime) : null)
)
)}
</TableCell>
<TableCell>
{isEditing ? (
<TextField
size="small"
type="number"
value={displayTruck?.loadingSequence ?? 0}
onChange={(e) => handleTruckFieldChange(index, "loadingSequence", parseInt(e.target.value) || 0)}
fullWidth
/>
) : (
truck.loadingSequence !== null && truck.loadingSequence !== undefined ? String(truck.loadingSequence) : "-"
)}
</TableCell>
<TableCell>
{isEditing ? (
<TextField
size="small"
type="number"
value={displayTruck?.districtReference ?? 0}
onChange={(e) => handleTruckFieldChange(index, "districtReference", parseInt(e.target.value) || 0)}
fullWidth
/>
) : (
truck.districtReference !== null && truck.districtReference !== undefined ? String(truck.districtReference) : "-"
)}
</TableCell>
<TableCell>
{isEditing ? (
<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>
) : (
normalizeStoreId(truck.storeId)
)}
</TableCell>
<TableCell>
{isEditing ? (
(() => {
const storeIdStr = normalizeStoreId(displayTruck?.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 ? t("Enter or select remark") : t("Not editable for this Store ID")}
disabled={!isEditable}
/>
)}
/>
);
})()
) : (
String(truck.remark || "-")
)}
</TableCell>
<TableCell>
<Stack direction="row" spacing={0.5}>
{isEditing ? (
truckData.map((truck, index) => (
<TableRow key={truck.id ?? `truck-${index}`}>
<TableCell>{String(truck.truckLanceCode || "-")}</TableCell>
<TableCell>
{formatDepartureTime(
Array.isArray(truck.departureTime)
? truck.departureTime
: truck.departureTime
? String(truck.departureTime)
: null
)}
</TableCell>
<TableCell>
{truck.loadingSequence !== null && truck.loadingSequence !== undefined
? String(truck.loadingSequence)
: "-"}
</TableCell>
<TableCell>
{truck.districtReference !== null && truck.districtReference !== undefined
? String(truck.districtReference)
: "-"}
</TableCell>
<TableCell>{normalizeStoreId(truck.storeId)}</TableCell>
<TableCell>{String(truck.remark || "-")}</TableCell>
<TableCell align="right">
<Stack direction="row" spacing={0.5} justifyContent="flex-end">
{truck.id && (
confirmingDeleteIndex === index ? (
<>
<IconButton
color="primary"
size="small"
onClick={() => handleSave(index)}
color="error"
onClick={() => handleDelete(truck.id!)}
disabled={saving}
title={t("Save changes")}
title={t("Confirm delete")}
>
<SaveIcon />
<CheckIcon />
</IconButton>
<IconButton
color="default"
size="small"
onClick={() => handleCancel(index)}
color="default"
onClick={() => setConfirmingDeleteIndex(null)}
disabled={saving}
title={t("Cancel editing")}
title={t("Cancel")}
>
<CancelIcon />
<CloseIcon />
</IconButton>
</>
) : (
<>
<IconButton
color="primary"
size="small"
onClick={() => handleEdit(index)}
disabled={editingRowIndex !== null}
title={t("Edit truck lane")}
>
<EditIcon />
</IconButton>
{truck.id && (
<IconButton
color="error"
size="small"
onClick={() => handleDelete(truck.id!)}
disabled={saving || editingRowIndex !== null}
title={t("Delete truck lane")}
>
<DeleteIcon />
</IconButton>
)}
</>
)}
</Stack>
</TableCell>
</TableRow>
);
})
<IconButton
color="error"
size="small"
onClick={() => setConfirmingDeleteIndex(index)}
disabled={saving || confirmingDeleteIndex !== null}
title={t("Delete truck lane")}
>
<DeleteIcon />
</IconButton>
)
)}
</Stack>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
@@ -737,97 +502,41 @@ const ShopDetail: React.FC = () => {
<Box sx={{ pt: 2 }}>
<Grid container spacing={2}>
<Grid item xs={12}>
<TextField
label={t("TruckLance Code")}
fullWidth
required
value={newTruck.truckLanceCode}
onChange={(e) => setNewTruck({ ...newTruck, truckLanceCode: e.target.value })}
disabled={saving}
/>
</Grid>
<Grid item xs={12}>
<TextField
label={t("Departure Time")}
type="time"
fullWidth
required
value={newTruck.departureTime}
onChange={(e) => setNewTruck({ ...newTruck, departureTime: e.target.value })}
disabled={saving}
InputLabelProps={{
shrink: true,
}}
inputProps={{
step: 300, // 5 minutes
<Autocomplete
options={availableTruckLanes}
loading={loadingTruckLanes}
value={selectedTruckLane}
onChange={(_event, newValue) => {
setSelectedTruckLane(newValue);
}}
getOptionLabel={(option) => getTruckLaneOptionLabel(option)}
isOptionEqualToValue={(option, value) => isSameTruckLaneOption(option, value)}
disabled={saving || loadingTruckLanes}
renderInput={(params) => (
<TextField
{...params}
label={t("TruckLance Code")}
required
placeholder={t("Select a truck lane")}
helperText={
!loadingTruckLanes && availableTruckLanes.length === 0
? t("No truck lanes available")
: undefined
}
/>
)}
/>
</Grid>
<Grid item xs={6}>
<Grid item xs={12}>
<TextField
label={t("Loading Sequence")}
type="number"
fullWidth
value={newTruck.loadingSequence}
onChange={(e) => setNewTruck({ ...newTruck, loadingSequence: parseInt(e.target.value) || 0 })}
value={addLoadingSequence}
onChange={(e) => setAddLoadingSequence(parseInt(e.target.value, 10) || 0)}
disabled={saving}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("District Reference")}
type="number"
fullWidth
value={newTruck.districtReference}
onChange={(e) => setNewTruck({ ...newTruck, districtReference: parseInt(e.target.value) || 0 })}
disabled={saving}
/>
</Grid>
<Grid item xs={6}>
<FormControl fullWidth>
<InputLabel>{t("Store ID")}</InputLabel>
<Select
value={newTruck.storeId}
label={t("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={t("Remark")}
fullWidth
placeholder={t("Enter or select remark")}
disabled={saving}
/>
)}
/>
</Grid>
)}
</Grid>
</Box>
</DialogContent>


+ 397
- 327
src/components/Shop/TruckLaneDetail.tsx
La diferencia del archivo ha sido suprimido porque es demasiado grande
Ver fichero


+ 16
- 1
src/i18n/en/common.json Ver fichero

@@ -48,5 +48,20 @@
"Upload row errors": "The following rows had issues:",
"item(s) updated": "item(s) updated.",
"Average unit price": "Average unit price",
"Latest market unit price": "Latest market unit price"
"Latest market unit price": "Latest market unit price",
"Invalid departure time": "Invalid departure time",
"No trucks found for this truck lane": "No trucks found for this truck lane",
"Departure time updated for all shops on this truck lane": "Departure time updated for all shops on this truck lane",
"Applies to all shops using this truck lane": "Applies to all shops using this truck lane",
"Please fill in the following required fields:": "Please fill in the following required fields:",
"Departure Time": "Departure Time",
"Submitting...": "Submitting...",
"Failed to save truck data": "Failed to save truck data",
"Edit departure time": "Edit departure time",
"Cancel editing": "Cancel editing",
"Select a shop first": "Select a shop first",
"Search or select branch": "Search or select branch",
"Mass Edit": "Mass Edit",
"Save All": "Save All",
"All shops updated successfully": "All shops updated successfully"
}

+ 10
- 0
src/i18n/zh/common.json Ver fichero

@@ -396,6 +396,11 @@
"Shop Information": "店鋪資訊",
"Shop Name": "店鋪名稱",
"Shop Branch": "店鋪分店",
"Select a shop first": "請先選擇店鋪",
"Search or select branch": "搜尋或選擇分店",
"Mass Edit": "批量編輯",
"Save All": "全部儲存",
"All shops updated successfully": "所有店鋪已成功更新",
"Shop Code": "店鋪編號",
"Truck Lane": "卡車路線",
"Truck Lane Detail": "卡車路線詳情",
@@ -458,6 +463,11 @@
"No Truck Lane data available": "沒有卡車路線資料",
"Please log in to view shop details": "請登入以查看店鋪詳情",
"Invalid truck data": "無效的卡車資料",
"Invalid departure time": "無效的出發時間",
"No trucks found for this truck lane": "此卡車路線沒有可更新的資料",
"Departure time updated for all shops on this truck lane": "已將出發時間更新至此卡車路線下的所有店鋪",
"Applies to all shops using this truck lane": "將套用至所有使用此卡車路線的店鋪",
"Edit departure time": "編輯出發時間",
"Failed to load truck lane detail": "載入卡車路線詳情失敗",
"Shop Detail": "店鋪詳情",
"Truck Lane Detail": "卡車路線詳情",


Cargando…
Cancelar
Guardar