Ver código fonte

po detail ui add deleted button and ui update

production
CANCERYS\kw093 19 horas atrás
pai
commit
fcd178d584
6 arquivos alterados com 432 adições e 313 exclusões
  1. +20
    -29
      src/components/PoDetail/PoDetail.tsx
  2. +1
    -1
      src/components/PoDetail/PoDetailWrapper.tsx
  3. +284
    -283
      src/components/PoDetail/PoInputGrid.tsx
  4. +115
    -0
      src/components/PoDetail/StockInLineRowActions.tsx
  5. +6
    -0
      src/i18n/en/purchaseOrder.json
  6. +6
    -0
      src/i18n/zh/purchaseOrder.json

+ 20
- 29
src/components/PoDetail/PoDetail.tsx Ver arquivo

@@ -749,8 +749,12 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
// onClick={(e) => e.stopPropagation()}
/>
</TableCell>
<TableCell align="left">{row.itemNo}</TableCell>
<TableCell align="left">{row.itemName}</TableCell>
<TableCell align="left" sx={{ width: 88, maxWidth: 88, px: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }} title={row.itemNo}>
{row.itemNo}
</TableCell>
<TableCell align="left" sx={{ width: 100, maxWidth: 100, px: 1, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }} title={row.itemName}>
{row.itemName}
</TableCell>
<TableCell align="right">{integerFormatter.format(row.qty)}</TableCell>
<TableCell align="right">{integerFormatter.format(row.processed)}</TableCell>
<TableCell align="left">{row.uom?.udfudesc}</TableCell>
@@ -1135,8 +1139,8 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
<TableHead>
<TableRow>
<TableCell align="center" sx={{ width: '60px' }}></TableCell>
<TableCell sx={{ width: '125px' }}>{t("itemNo")}</TableCell>
<TableCell align="left" sx={{ width: '125px' }}>{t("itemName")}</TableCell>
<TableCell sx={{ width: '88px', maxWidth: '88px', px: 1 }}>{t("itemNo")}</TableCell>
<TableCell align="left" sx={{ width: '100px', maxWidth: '100px', px: 1 }}>{t("itemName")}</TableCell>
<TableCell align="right">{t("qty")}</TableCell>
<TableCell align="right">{t("processedQty")}</TableCell>
<TableCell align="left">{t("uom")}</TableCell>
@@ -1168,32 +1172,19 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
{selectedRow ? `已選擇貨品: ${selectedRow?.itemNo ? selectedRow.itemNo : 'N/A'} - ${selectedRow?.itemName ? selectedRow?.itemName : 'N/A'}` : "未選擇貨品"}
</Typography>
</Grid>
<Grid item xs={12}>
<Grid item xs={12} sx={{ minWidth: 0 }}>
{selectedRow && (
<TableContainer component={Paper} sx={{ width: 'fit-content', overflow: 'auto' }}>
<Table>
<TableBody>
<TableRow>
<TableCell align="right">
<Box>
<PoInputGrid
// qc={qc}
setRows={setRows}
stockInLine={stockInLine}
setStockInLine={setStockInLine}
setProcessedQty={setProcessedQty}
itemDetail={selectedRow}
warehouse={warehouse}
fetchPoDetail={fetchPoDetail}
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine}
printerCombo={printerCombo}
/>
</Box>
</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
<PoInputGrid
setRows={setRows}
stockInLine={stockInLine}
setStockInLine={setStockInLine}
setProcessedQty={setProcessedQty}
itemDetail={selectedRow}
warehouse={warehouse}
fetchPoDetail={fetchPoDetail}
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine}
printerCombo={printerCombo}
/>
)}
</Grid>
</Grid>


+ 1
- 1
src/components/PoDetail/PoDetailWrapper.tsx Ver arquivo

@@ -32,7 +32,7 @@ const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => {
fetchPrinterCombo(),
]);
// const poWithStockInLine = await fetchPoWithStockInLines(id)
console.log("%c pol:", "color:green", poWithStockInLine);
//console.log("%c pol:", "color:green", poWithStockInLine);
return <PoDetail po={poWithStockInLine} warehouse={warehouse} printerCombo={printerCombo} />;
};



+ 284
- 283
src/components/PoDetail/PoInputGrid.tsx Ver arquivo

@@ -1,7 +1,6 @@
"use client";
import {
FooterPropsOverrides,
GridActionsCellItem,
GridCellParams,
GridRowId,
GridRowIdGetter,
@@ -19,11 +18,12 @@ import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import StyledDataGrid from "../StyledDataGrid";
import { GridColDef } from "@mui/x-data-grid";
import { Box, Button, Grid, Typography } from "@mui/material";
import { Box, Button, Grid, Typography, useMediaQuery, useTheme } from "@mui/material";
import { useTranslation } from "react-i18next";
import { Add } from "@mui/icons-material";
import SaveIcon from "@mui/icons-material/Save";
@@ -35,7 +35,7 @@ import ShoppingCartIcon from "@mui/icons-material/ShoppingCart";
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
import { PurchaseOrderLine } from "@/app/api/po";
import { StockInLine } from "@/app/api/stockIn";
import { createStockInLine, QcResult } from "@/app/api/stockIn/actions";
import { createStockInLine, deleteStockInLine, QcResult } from "@/app/api/stockIn/actions";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import {
returnWeightUnit,
@@ -67,6 +67,63 @@ import { EscalationResult } from "@/app/api/escalation";
import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions";
import { SessionWithTokens } from "@/config/authConfig";
import { EscalationCombo } from "@/app/api/user";
import { deleteDialog } from "../Swal/CustomAlerts";
import StockInLineRowActions from "./StockInLineRowActions";

/** Sum of fixed column widths (desktop) so the grid can scroll horizontally without squeezing cells. */
const STOCK_IN_GRID_MIN_WIDTH_DESKTOP = 1062;

const ACTIONS_COLUMN_WIDTH = 168;

const ACTION_BUTTON_HEIGHT = 38;
const ACTION_BUTTON_GAP = 6;
/** Extra space for cell padding + outlined button borders */
const ACTION_ROW_EXTRA_PADDING = 36;

function getActionRowHeight(buttonCount: number): number {
return (
buttonCount * ACTION_BUTTON_HEIGHT +
Math.max(0, buttonCount - 1) * ACTION_BUTTON_GAP +
ACTION_ROW_EXTRA_PADDING
);
}

function countActionButtonsForRow(row: StockInLineRow): number {
let count = 1;
const status = (row.status ?? "").toLowerCase();
if (status === "rejected" || status === "partially_completed") {
count += 1;
}
if (status === "received") {
count += 1;
}
if (canDeleteStockInLine(row)) {
count += 1;
}
return count;
}

/** Tighter horizontal padding for narrow data columns (headers unchanged). */
const COMPACT_STOCK_IN_CELL_FIELDS = [
"dnNo",
"productLotNo",
"purchaseAcceptedQty",
"uom",
"stockUom",
"status",
] as const;

function canDeleteStockInLine(sil: StockInLineRow): boolean {
if (sil._isNew || sil.status === "draft") {
return true;
}
const hasPutAway = (sil.putAwayLines ?? []).some(
(p) => Number(p.stockQty ?? p.qty ?? 0) > 0,
);
if (hasPutAway) return false;
const status = (sil.status ?? "").toLowerCase();
return status !== "completed" && status !== "partially_completed";
}

interface ResultWithId {
id: number;
@@ -80,7 +137,7 @@ interface Props {
itemDetail: PurchaseOrderLine;
stockInLine: StockInLine[];
warehouse: WarehouseResult[];
fetchPoDetail: (poId: string) => void;
fetchPoDetail: (poId: string, preserveDnNo?: boolean, preferredPolId?: number) => void;
handleMailTemplateForStockInLine: (stockInLineId: number) => void;
printerCombo: PrinterCombo[];
}
@@ -127,6 +184,11 @@ function PoInputGrid({
}: Props) {

const { t } = useTranslation("purchaseOrder");
const theme = useTheme();
/** Narrow phones: hide low-priority columns. */
const isCompact = useMediaQuery(theme.breakpoints.down("md"), { noSsr: true });
/** Tablet / sub-desktop (< xl): flex columns to fill available width. Desktop (≥ xl) keeps fixed widths. */
const isTablet = useMediaQuery(theme.breakpoints.down("xl"), { noSsr: true });
const apiRef = useGridApiRef();
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
const getRowId = useCallback<GridRowIdGetter<StockInLineRow>>(
@@ -151,6 +213,8 @@ function PoInputGrid({
const [putAwayOpen, setPutAwayOpen] = useState(false);
const [rejectOpen, setRejectOpen] = useState(false);
const [btnIsLoading, setBtnIsLoading] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const deleteInFlightRef = useRef(false);
const [currQty, setCurrQty] = useState(() => {
const total = entries.reduce(
// remaining qty (M18 unit)
@@ -180,6 +244,42 @@ function PoInputGrid({
},
[getRowId],
);

const handleSoftDelete = useCallback(
(row: StockInLineRow) => {
if (deleteInFlightRef.current || isDeleting) return;

const rowId = row.id as number;
const isDraft = row._isNew || row.status === "draft";

const doDelete = async () => {
if (deleteInFlightRef.current) return;
deleteInFlightRef.current = true;
setIsDeleting(true);
try {
if (isDraft) {
handleDelete(rowId)();
return;
}
await deleteStockInLine(rowId);
await fetchPoDetail(
String(itemDetail.purchaseOrderId),
true,
itemDetail.id,
);
} catch (error) {
console.error("Failed to delete stock in line:", error);
alert(t("Cannot delete put away record"));
} finally {
setIsDeleting(false);
deleteInFlightRef.current = false;
}
};

void deleteDialog(doDelete, t);
},
[fetchPoDetail, handleDelete, isDeleting, itemDetail.id, itemDetail.purchaseOrderId, t],
);
const closeQcModal = useCallback(() => {
setQcOpen(false);
@@ -445,20 +545,37 @@ function PoInputGrid({

const getButtonSx = (sil : StockInLine) => {
const status = sil?.status?.toLowerCase();
let btnSx = {label:"", color:""};
let btnSx = { label: "", color: "" };
switch (status) {
case "received": btnSx = {label: t("view putaway"), color:"secondary.main"}; break;
case "escalated": if (sessionToken?.id == sil?.handlerId) {
btnSx = {label: t("escalation processing"), color:"warning.main"};
break;}
case "rejected":
case "received":
btnSx = { label: t("view putaway"), color: "secondary.main" };
break;
case "escalated":
if (sessionToken?.id == sil?.handlerId) {
btnSx = { label: t("escalation processing"), color: "warning.main" };
break;
}
btnSx = { label: t("qc processing"), color: "success.main" };
break;
case "rejected":
case "partially_completed":
case "completed": btnSx = {label: t("view stockin"), color:"info.main"}; break;
default: btnSx = {label: t("qc processing"), color:"success.main"};
case "completed":
btnSx = { label: t("view stockin"), color: "info.main" };
break;
default:
btnSx = { label: t("qc processing"), color: "success.main" };
}
return btnSx
return btnSx;
};

const columnVisibilityModel = useMemo(
() => ({
uom: !isCompact,
stockUom: !isCompact,
}),
[isCompact],
);

// const handleQrCode = useCallback(
// (id: GridRowId, params: any) => () => {
// setRowModesModel((prev) => ({
@@ -478,333 +595,172 @@ function PoInputGrid({
// [printQrcode],
// );

const columns = useMemo<GridColDef[]>(
() => [
// {
// field: "itemNo",
// headerName: t("itemNo"),
// width: 100,
// // flex: 0.4,
// },
const columns = useMemo<GridColDef[]>(() => {
const baseColumns: GridColDef[] = [
{
field: "dnNo",
headerName: t("dnNo"),
width: 125,
// renderCell: () => {
// return <>DN0000001</>
// }
// flex: 0.4,
width: 92,
},
{
field: "receiptDate",
headerName: t("receiptDate"),
width: 125,
renderCell: (params) => {
// console.log(params.row)
// return <>07/08/2025</>
return arrayToDateString(params.value)
}
// flex: 0.4,
renderCell: (params) => arrayToDateString(params.value),
},
{
field: "productLotNo",
headerName: t("productLotNo"),
width: 125,
width: 100,
},
// {
// field: "itemName",
// headerName: t("itemName"),
// width: 100,
// // flex: 0.6,
// },
{
field: "purchaseAcceptedQty",
headerName: t("acceptedQty"),
// flex: 0.5,
width: 125,
width: 84,
align: "right",
headerAlign: "right",
type: "number",
// editable: true,
// replace with tooltip + content
renderCell: (params) => {
const qty = params.row.purchaseAcceptedQty ?? 0;
return integerFormatter.format(qty);
}
},
},
{
field: "uom",
headerName: t("uom"),
width: 150,
// flex: 0.5,
renderCell: (params) => {
return itemDetail.uom?.udfudesc;
width: 156,
renderCell: () => {
const text = itemDetail.uom?.udfudesc ?? "-";
return (
<Box
sx={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
width: "100%",
}}
title={text}
>
{text}
</Box>
);
},
},
{
field: "stockQty",
headerName: t("Stock In Qty"),
// flex: 0.5,
width: 125,
type: "number",
// editable: true,
// replace with tooltip + content
renderCell: (params) => {
// acceptedQty 現在就是庫存單位數量
const stockQty = params.row.acceptedQty ?? 0;
return decimalFormatter.format(stockQty);
}
},
},
{
field: "stockUom",
headerName: t("Stock UoM"),
width: 150,
// flex: 0.5,
renderCell: (params) => {
return itemDetail.stockUom.stockUomDesc;
width: 124,
renderCell: () => {
const text = itemDetail.stockUom.stockUomDesc ?? "-";
return (
<Box
sx={{
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
width: "100%",
}}
title={text}
>
{text}
</Box>
);
},
},
// {
// field: "weight",
// headerName: t("weight"),
// width: 120,
// // flex: 0.5,
// renderCell: (params) => {
// const weight = calculateWeight(
// params.row.acceptedQty,
// params.row.uom,
// );
// const weightUnit = returnWeightUnit(params.row.uom);
// return `${decimalFormatter.format(weight)} ${weightUnit}`;
// },
// },
{
field: "status",
headerName: t("Status"),
width: 140,
// flex: 0.5,
width: 88,
renderCell: (params) => {
const handlerId = params.row.handlerId
const status = params.row.status
return (<span style={{
color:
(status == "escalated")? "red":
(status == "rejected" || status == "partially_completed") ? "orange" : "inherit"}}
const status = params.row.status;
return (
<span
style={{
color:
status == "escalated"
? "red"
: status == "rejected" || status == "partially_completed"
? "orange"
: "inherit",
}}
>
{t(`${params.row.status}`)}
</span>);
</span>
);
},
},
{
field: "actions",
type: "actions",
// headerName: `${t("start")} | ${t("qc")} | ${t("escalation")} | ${t(
// "stock in",
// )} | ${t("putaway")} | ${t("delete")}`,
headerName: "操作",
// headerName: "start | qc | escalation | stock in | putaway | delete",
width: 350, //200
// flex: 2,
width: ACTIONS_COLUMN_WIDTH,
sortable: false,
filterable: false,
disableColumnMenu: true,
cellClassName: "actions",
getActions: (params) => {
const data = params.row;
// console.log(params.row.status);
renderCell: (params) => {
const data = params.row as StockInLineRow;
const btnSx = getButtonSx(data);
// console.log(stockInLineStatusMap[status]);
// console.log(session?.user?.abilities?.includes("APPROVAL"));
return [
<GridActionsCellItem
icon={<Button variant="contained" sx={{ width: '150px', backgroundColor: btnSx.color }}>
{btnSx.label}</Button>}
label="start"
sx={{
// color: "primary.main",
// marginRight: 1,
const status = (data.status ?? "").toLowerCase();
const canEmail =
status === "rejected" || status === "partially_completed";
const canPrint = status === "received";
const canDelete = canDeleteStockInLine(data);

return (
<StockInLineRowActions
btnSx={btnSx}
onPrimaryClick={() => {
void handleNewQC(params.row.id, params)();
}}
onClick={handleNewQC(params.row.id, params)}
color="inherit"
key={`edit`}
/>,
<GridActionsCellItem
icon={
data.status !== "received" ?
(<Button
id="emailSupplier"
type="button"
variant="contained"
color="primary"
sx={{ width: '150px' }}
disabled={params.row.status != "rejected" && params.row.status != "partially_completed"}
onClick={() => handleMailTemplateForStockInLine(params.row.id as number)}
>
{t("email supplier")}
</Button>) : (
<Button
id="printQrCode"
type="button"
variant="contained"
// color="red"
sx={{ width: '150px', backgroundColor: '#7f434a' }}
disabled={btnIsLoading || data.status != "received"}
onClick={() => printQrcode(params.row)}
>
{t("printQrCode")}
</Button>
)
canEmail={canEmail}
canPrint={canPrint}
canDelete={canDelete}
onEmail={() =>
handleMailTemplateForStockInLine(params.row.id as number)
}
label="start"
sx={{
// color: "primary.main",
// marginRight: 1,
}}
// onClick={handleNewQC(params.row.id, params)}
color="inherit"
key="edit"
/>,
// <GridActionsCellItem
// icon={<Button variant="contained">{t("putawayBtn")}</Button>}
// label="start"
// sx={{
// color: "primary.main",
// // marginRight: 1,
// }}
// // disabled={!(stockInLineStatusMap[status] === 0)}
// // set _isNew to false after posting
// // or check status
// onClick={handleStart(params.row.id, params)}
// color="inherit"
// key="edit"
// />,

// <GridActionsCellItem
// icon={<Button variant="contained">{t("qc processing")}</Button>}
// label="start"
// sx={{
// color: "primary.main",
// // marginRight: 1,
// }}
// disabled={!(stockInLineStatusMap[status] === 0)}
// // set _isNew to false after posting
// // or check status
// onClick={handleStart(params.row.id, params)}
// color="inherit"
// key="edit"
// />,
// <GridActionsCellItem
// icon={<FactCheckIcon />}
// label="qc"
// sx={{
// color: "primary.main",
// // marginRight: 1,
// }}
// disabled={
// // stockInLineStatusMap[status] === 9 ||
// stockInLineStatusMap[status] < 1
// }
// // set _isNew to false after posting
// // or check status
// onClick={handleQC(params.row.id, params)}
// color="inherit"
// key="edit"
// />,
// <GridActionsCellItem
// icon={<NotificationImportantIcon />}
// label="escalation"
// sx={{
// color: "primary.main",
// // marginRight: 1,
// }}
// disabled={
// stockInLineStatusMap[status] === 9 ||
// stockInLineStatusMap[status] <= 0 ||
// stockInLineStatusMap[status] >= 5
// }
// // set _isNew to false after posting
// // or check status
// onClick={handleEscalation(params.row.id, params)}
// color="inherit"
// key="edit"
// />,
// <GridActionsCellItem
// icon={<ShoppingCartIcon />}
// label="stockin"
// sx={{
// color: "primary.main",
// // marginRight: 1,
// }}
// disabled={
// stockInLineStatusMap[status] === 9 ||
// stockInLineStatusMap[status] <= 2 ||
// stockInLineStatusMap[status] >= 7 ||
// (stockInLineStatusMap[status] >= 3 &&
// stockInLineStatusMap[status] <= 5 &&
// !session?.user?.abilities?.includes("APPROVAL"))
// }
// // set _isNew to false after posting
// // or check status
// onClick={handleStockIn(params.row.id, params)}
// color="inherit"
// key="edit"
// />,
// <GridActionsCellItem
// icon={<ShoppingCartIcon />}
// label="putaway"
// sx={{
// color: "primary.main",
// // marginRight: 1,
// }}
// disabled={
// stockInLineStatusMap[status] === 9 ||
// stockInLineStatusMap[status] < 7
// }
// // set _isNew to false after posting
// // or check status
// onClick={handlePutAway(params.row.id, params)}
// color="inherit"
// key="edit"
// />,
// // <GridActionsCellItem
// // icon={<QrCodeIcon />}
// // label="putaway"
// // sx={{
// // color: "primary.main",
// // // marginRight: 1,
// // }}
// // disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8}
// // // set _isNew to false after posting
// // // or check status
// // onClick={handleQrCode(params.row.id, params)}
// // color="inherit"
// // key="edit"
// // />,
// <GridActionsCellItem
// icon={
// stockInLineStatusMap[status] >= 1 ? (
// <DoDisturbIcon />
// ) : (
// <DeleteIcon />
// )
// }
// label="Delete"
// sx={{
// color: "error.main",
// }}
// disabled={
// stockInLineStatusMap[status] >= 7 &&
// stockInLineStatusMap[status] <= 9
// }
// onClick={
// stockInLineStatusMap[status] === 0
// ? handleDelete(params.row.id)
// : handleReject(params.row.id, params)
// }
// color="inherit"
// key="edit"
// />,
];
onPrint={() => printQrcode(params.row)}
onDelete={() => handleSoftDelete(data)}
btnIsLoading={btnIsLoading}
isDeleting={isDeleting}
/>
);
},
},
],
[t, handleStart, handleEscalation, handleStockIn, handlePutAway, handleDelete, handleReject, itemDetail],
);
];

if (!isTablet) {
return baseColumns;
}

return baseColumns.map((col) => {
if (col.field === "actions") {
return { ...col, flex: 0, width: ACTIONS_COLUMN_WIDTH };
}
const minWidth = col.width ?? 80;
return { ...col, flex: 1, minWidth, width: undefined };
});
}, [
t,
isTablet,
itemDetail,
handleNewQC,
handleMailTemplateForStockInLine,
printQrcode,
handleSoftDelete,
btnIsLoading,
isDeleting,
sessionToken?.id,
]);

const unsortableColumns = useMemo(() =>
columns.map(column => ({ ...column, sortable: false }))
@@ -915,14 +871,36 @@ function PoInputGrid({
</Box> */}
</>
);

const getRowHeight = useCallback(
(params: { model: StockInLineRow }) => {
const count = countActionButtonsForRow(params.model);
return getActionRowHeight(count);
},
[],
);
return (
<>
<Box
sx={{
width: "100%",
maxWidth: "100%",
minWidth: 0,
overflowX: "auto",
WebkitOverflowScrolling: "touch",
pb: 1,
}}
>
<StyledDataGrid
getRowId={getRowId}
apiRef={apiRef}
autoHeight
getRowHeight={getRowHeight}
columnVisibilityModel={columnVisibilityModel}
sx={{
width: "100%",
minWidth: isTablet ? undefined : STOCK_IN_GRID_MIN_WIDTH_DESKTOP,
"--DataGrid-overlayHeight": "100px",
".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
border: "1px solid",
@@ -932,6 +910,28 @@ function PoInputGrid({
border: "1px solid",
borderColor: "warning.main",
},
"& .MuiDataGrid-cell.actions": {
overflow: "visible",
alignItems: "flex-start",
py: 0.75,
lineHeight: "normal",
},
"& .MuiDataGrid-cell[data-field='actions']": {
py: 0.75,
px: 1,
},
...Object.fromEntries(
COMPACT_STOCK_IN_CELL_FIELDS.flatMap((field) => [
[
`& .MuiDataGrid-cell[data-field="${field}"]`,
{ px: 1 },
],
[
`& .MuiDataGrid-columnHeader[data-field="${field}"]`,
{ px: 1 },
],
]),
),
}}
disableColumnMenu
editMode="row"
@@ -963,6 +963,7 @@ function PoInputGrid({
footer: { child: footer },
}}
/>
</Box>
{/* {modalInfo !== undefined && ( */}
<>
<QcStockInModal


+ 115
- 0
src/components/PoDetail/StockInLineRowActions.tsx Ver arquivo

@@ -0,0 +1,115 @@
"use client";

import { Box, Button } from "@mui/material";
import { useTranslation } from "react-i18next";

export type StockInLineActionStyle = {
label: string;
color: string;
};

type Props = {
btnSx: StockInLineActionStyle;
onPrimaryClick: () => void;
canEmail: boolean;
canPrint: boolean;
canDelete: boolean;
onEmail: () => void;
onPrint: () => void;
onDelete: () => void;
btnIsLoading: boolean;
isDeleting: boolean;
};

export default function StockInLineRowActions({
btnSx,
onPrimaryClick,
canEmail,
canPrint,
canDelete,
onEmail,
onPrint,
onDelete,
btnIsLoading,
isDeleting,
}: Props) {
const { t } = useTranslation("purchaseOrder");

const buttonSx = {
whiteSpace: "nowrap" as const,
fontSize: 14,
px: 1.5,
py: 0.75,
minHeight: 34,
width: "100%",
justifyContent: "center",
};

return (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "stretch",
gap: 0.75,
width: "100%",
py: 0.5,
boxSizing: "border-box",
}}
onClick={(e) => e.stopPropagation()}
>
<Button
variant="contained"
size="small"
sx={{
...buttonSx,
backgroundColor: btnSx.color,
}}
onClick={onPrimaryClick}
>
{btnSx.label}
</Button>
{canEmail && (
<Button
id="emailSupplier"
type="button"
variant="contained"
color="primary"
size="small"
sx={buttonSx}
onClick={onEmail}
>
{t("email supplier")}
</Button>
)}
{canPrint && (
<Button
id="printQrCode"
type="button"
variant="contained"
size="small"
sx={{
...buttonSx,
backgroundColor: "#7f434a",
}}
disabled={btnIsLoading}
onClick={onPrint}
>
{t("printQrCode")}
</Button>
)}
{canDelete && (
<Button
variant="outlined"
color="error"
size="small"
sx={buttonSx}
disabled={isDeleting || btnIsLoading}
onClick={onDelete}
>
{t("delete")}
</Button>
)}
</Box>
);
}

+ 6
- 0
src/i18n/en/purchaseOrder.json Ver arquivo

@@ -62,6 +62,12 @@
"stock in": "Stock In",
"putaway": "Put Away",
"delete": "Delete",
"actionView": "View",
"actionProcess": "Process",
"moreActions": "More actions",
"Do you want to delete?": "Do you want to delete this delivery record?",
"Delete": "Delete",
"Cannot delete put away record": "Cannot delete a record that has already been put away",
"Accept quantity must be greater than 0": "Accept quantity must be greater than 0",
"QC items without result": "QC items without result",
"Failed items must have failed quantity": "Failed items must have failed quantity",


+ 6
- 0
src/i18n/zh/purchaseOrder.json Ver arquivo

@@ -62,6 +62,12 @@
"stock in": "入庫",
"putaway": "上架",
"delete": "刪除",
"actionView": "查看",
"actionProcess": "處理",
"moreActions": "更多操作",
"Do you want to delete?": "確定要刪除此來貨記錄嗎?",
"Delete": "刪除",
"Cannot delete put away record": "已上架的來貨記錄不可刪除",
"Accept quantity must be greater than 0": "揀收數量不能少於1",
"QC items without result": "有未完成品檢項目",
"Failed items must have failed quantity": "請輸入不合格數量",


Carregando…
Cancelar
Salvar