|
|
|
@@ -2,12 +2,13 @@ |
|
|
|
|
|
|
|
import { useCallback, useMemo, useState } from "react"; |
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
|
import SearchResults, { Column } from "../SearchResults/index"; |
|
|
|
import SearchResults, { Column } from "../SearchResults/SearchResults"; |
|
|
|
import DeleteIcon from "@mui/icons-material/Delete"; |
|
|
|
import EditIcon from "@mui/icons-material/Edit"; |
|
|
|
import { useRouter } from "next/navigation"; |
|
|
|
import { deleteDialog, successDialog } from "../Swal/CustomAlerts"; |
|
|
|
import { WarehouseResult } from "@/app/api/warehouse"; |
|
|
|
import { deleteWarehouse } from "@/app/api/warehouse/actions"; |
|
|
|
import { deleteWarehouse, editWarehouse } from "@/app/api/warehouse/actions"; |
|
|
|
import Card from "@mui/material/Card"; |
|
|
|
import CardContent from "@mui/material/CardContent"; |
|
|
|
import CardActions from "@mui/material/CardActions"; |
|
|
|
@@ -18,6 +19,10 @@ import Box from "@mui/material/Box"; |
|
|
|
import RestartAlt from "@mui/icons-material/RestartAlt"; |
|
|
|
import Search from "@mui/icons-material/Search"; |
|
|
|
import InputAdornment from "@mui/material/InputAdornment"; |
|
|
|
import Dialog from "@mui/material/Dialog"; |
|
|
|
import DialogTitle from "@mui/material/DialogTitle"; |
|
|
|
import DialogContent from "@mui/material/DialogContent"; |
|
|
|
import DialogActions from "@mui/material/DialogActions"; |
|
|
|
|
|
|
|
interface Props { |
|
|
|
warehouses: WarehouseResult[]; |
|
|
|
@@ -36,6 +41,15 @@ const WarehouseHandle: React.FC<Props> = ({ warehouses }) => { |
|
|
|
const router = useRouter(); |
|
|
|
const [isSearching, setIsSearching] = useState(false); |
|
|
|
|
|
|
|
// State for editing order & stockTakeSection |
|
|
|
const [editingWarehouse, setEditingWarehouse] = useState<WarehouseResult | null>(null); |
|
|
|
const [editValues, setEditValues] = useState({ |
|
|
|
order: "", |
|
|
|
stockTakeSection: "", |
|
|
|
}); |
|
|
|
const [isSavingEdit, setIsSavingEdit] = useState(false); |
|
|
|
const [editError, setEditError] = useState(""); |
|
|
|
|
|
|
|
const [searchInputs, setSearchInputs] = useState({ |
|
|
|
store_id: "", |
|
|
|
warehouse: "", |
|
|
|
@@ -69,6 +83,71 @@ const WarehouseHandle: React.FC<Props> = ({ warehouses }) => { |
|
|
|
setPagingController({ pageNum: 1, pageSize: pagingController.pageSize }); |
|
|
|
}, [warehouses, pagingController.pageSize]); |
|
|
|
|
|
|
|
const onEditClick = useCallback((warehouse: WarehouseResult) => { |
|
|
|
setEditingWarehouse(warehouse); |
|
|
|
setEditValues({ |
|
|
|
order: warehouse.order ?? "", |
|
|
|
stockTakeSection: warehouse.stockTakeSection ?? "", |
|
|
|
}); |
|
|
|
setEditError(""); |
|
|
|
}, []); |
|
|
|
|
|
|
|
const handleEditClose = useCallback(() => { |
|
|
|
if (isSavingEdit) return; |
|
|
|
setEditingWarehouse(null); |
|
|
|
setEditError(""); |
|
|
|
}, [isSavingEdit]); |
|
|
|
|
|
|
|
const handleEditSave = useCallback(async () => { |
|
|
|
if (!editingWarehouse) return; |
|
|
|
|
|
|
|
const trimmedOrder = editValues.order.trim(); |
|
|
|
const trimmedStockTakeSection = editValues.stockTakeSection.trim(); |
|
|
|
|
|
|
|
const orderPattern = /^[A-Za-z0-9]{2}-[A-Za-z0-9]{3}$/; |
|
|
|
const sectionPattern = /^[A-Za-z0-9]{2}-[A-Za-z0-9]{3}$/; |
|
|
|
|
|
|
|
if (trimmedOrder && !orderPattern.test(trimmedOrder)) { |
|
|
|
setEditError(`${t("order")} 格式必須為 XF-YYY`); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (trimmedStockTakeSection && !sectionPattern.test(trimmedStockTakeSection)) { |
|
|
|
setEditError(`${t("stockTakeSection")} 格式必須為 ST-YYY`); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
setIsSavingEdit(true); |
|
|
|
setEditError(""); |
|
|
|
|
|
|
|
await editWarehouse(editingWarehouse.id, { |
|
|
|
order: trimmedOrder || undefined, |
|
|
|
stockTakeSection: trimmedStockTakeSection || undefined, |
|
|
|
}); |
|
|
|
|
|
|
|
setFilteredWarehouse((prev) => |
|
|
|
prev.map((w) => |
|
|
|
w.id === editingWarehouse.id |
|
|
|
? { |
|
|
|
...w, |
|
|
|
order: trimmedOrder || undefined, |
|
|
|
stockTakeSection: trimmedStockTakeSection || undefined, |
|
|
|
} |
|
|
|
: w, |
|
|
|
), |
|
|
|
); |
|
|
|
|
|
|
|
router.refresh(); |
|
|
|
setEditingWarehouse(null); |
|
|
|
} catch (error) { |
|
|
|
console.error("Failed to edit warehouse:", error); |
|
|
|
setEditError(t("An error has occurred. Please try again later.")); |
|
|
|
} finally { |
|
|
|
setIsSavingEdit(false); |
|
|
|
} |
|
|
|
}, [editValues, editingWarehouse, router, t, setFilteredWarehouse]); |
|
|
|
|
|
|
|
const handleSearch = useCallback(() => { |
|
|
|
setIsSearching(true); |
|
|
|
try { |
|
|
|
@@ -177,6 +256,14 @@ const WarehouseHandle: React.FC<Props> = ({ warehouses }) => { |
|
|
|
|
|
|
|
const columns = useMemo<Column<WarehouseResult>[]>( |
|
|
|
() => [ |
|
|
|
{ |
|
|
|
name: "action", |
|
|
|
label: t("Edit"), |
|
|
|
onClick: onEditClick, |
|
|
|
buttonIcon: <EditIcon />, |
|
|
|
color: "primary", |
|
|
|
sx: { width: "10%", minWidth: "80px" }, |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "code", |
|
|
|
label: t("code"), |
|
|
|
@@ -226,6 +313,7 @@ const WarehouseHandle: React.FC<Props> = ({ warehouses }) => { |
|
|
|
headerAlign: "left", |
|
|
|
sx: { width: "15%", minWidth: "120px" }, |
|
|
|
}, |
|
|
|
|
|
|
|
{ |
|
|
|
name: "action", |
|
|
|
label: t("Delete"), |
|
|
|
@@ -338,6 +426,51 @@ const WarehouseHandle: React.FC<Props> = ({ warehouses }) => { |
|
|
|
pagingController={pagingController} |
|
|
|
setPagingController={setPagingController} |
|
|
|
/> |
|
|
|
<Dialog |
|
|
|
open={Boolean(editingWarehouse)} |
|
|
|
onClose={handleEditClose} |
|
|
|
fullWidth |
|
|
|
maxWidth="sm" |
|
|
|
> |
|
|
|
<DialogTitle>{t("Edit")}</DialogTitle> |
|
|
|
<DialogContent sx={{ pt: 2, display: "flex", flexDirection: "column", gap: 2 }}> |
|
|
|
{editError && ( |
|
|
|
<Typography variant="body2" color="error"> |
|
|
|
{editError} |
|
|
|
</Typography> |
|
|
|
)} |
|
|
|
<TextField |
|
|
|
label={t("order")} |
|
|
|
value={editValues.order} |
|
|
|
onChange={(e) => |
|
|
|
setEditValues((prev) => ({ ...prev, order: e.target.value })) |
|
|
|
} |
|
|
|
size="small" |
|
|
|
fullWidth |
|
|
|
/> |
|
|
|
<TextField |
|
|
|
label={t("stockTakeSection")} |
|
|
|
value={editValues.stockTakeSection} |
|
|
|
onChange={(e) => |
|
|
|
setEditValues((prev) => ({ ...prev, stockTakeSection: e.target.value })) |
|
|
|
} |
|
|
|
size="small" |
|
|
|
fullWidth |
|
|
|
/> |
|
|
|
</DialogContent> |
|
|
|
<DialogActions> |
|
|
|
<Button onClick={handleEditClose} disabled={isSavingEdit}> |
|
|
|
{t("Cancel")} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
onClick={handleEditSave} |
|
|
|
disabled={isSavingEdit} |
|
|
|
variant="contained" |
|
|
|
> |
|
|
|
{t("Save", { ns: "common" })} |
|
|
|
</Button> |
|
|
|
</DialogActions> |
|
|
|
</Dialog> |
|
|
|
</> |
|
|
|
); |
|
|
|
}; |
|
|
|
|