ソースを参照

update

master
MSI\derek 2週間前
コミット
cce567983c
15個のファイルの変更1475行の追加127行の削除
  1. +1
    -1
      src/app/(main)/layout.tsx
  2. +1
    -0
      src/app/api/inventory/index.ts
  3. +2
    -0
      src/app/api/po/actions.ts
  4. +5
    -1
      src/app/global.css
  5. +4
    -0
      src/components/DetailedScheduleDetail/ViewByFGDetails.tsx
  6. +94
    -10
      src/components/PoDetail/PoDetail.tsx
  7. +214
    -114
      src/components/PoDetail/PoInputGrid.tsx
  8. +400
    -0
      src/components/PoDetail/QCDatagrid.tsx
  9. +242
    -0
      src/components/PoDetail/QcFormVer2.tsx
  10. +149
    -0
      src/components/PoDetail/QcStockInModalVer2.tsx
  11. +323
    -0
      src/components/PoDetail/StockInFormVer2.tsx
  12. +23
    -0
      src/components/PoDetail/dummyQcTemplate.tsx
  13. +1
    -1
      src/components/RoughSchedule/RoughSchedileSearchView.tsx
  14. +3
    -0
      src/components/StyledDataGrid/StyledDataGrid.tsx
  15. +13
    -0
      src/i18n/zh/purchaseOrder.json

+ 1
- 1
src/app/(main)/layout.tsx ファイルの表示

@@ -13,7 +13,7 @@ import { UploadProvider } from "@/components/UploadProvider/UploadProvider";
import SessionProviderWrapper from "@/components/SessionProviderWrapper/SessionProviderWrapper";
import QrCodeScannerProvider from "@/components/QrCodeScannerProvider/QrCodeScannerProvider";
import { I18nProvider } from "@/i18n";
import "src/app/global.css"
export default async function MainLayout({
children,
}: {


+ 1
- 0
src/app/api/inventory/index.ts ファイルの表示

@@ -39,6 +39,7 @@ export interface InventoryLotLineResult {
uom: string;
qtyPerSmallestUnit: number;
baseUom: string;
stockInLineId: number
}

export interface InventoryLotLineItem {


+ 2
- 0
src/app/api/po/actions.ts ファイルの表示

@@ -38,8 +38,10 @@ export interface PurchaseQcResult {
}
export interface StockInInput {
status: string;
poCode: string;
productLotNo?: string;
dnNo?: string;
itemName: string;
invoiceNo?: string;
receiptDate: string;
acceptedQty: number;


+ 5
- 1
src/app/global.css ファイルの表示

@@ -1,3 +1,7 @@

@tailwind components;
@tailwind utilities;
@tailwind utilities;

html, body {
overscroll-behavior: none;
}

+ 4
- 0
src/components/DetailedScheduleDetail/ViewByFGDetails.tsx ファイルの表示

@@ -160,6 +160,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick
type: "input-number",
style: {
textAlign: "right",
// width: "100px",
},
renderCell: (row) => {
if (typeof row.demandQty == "number") {
@@ -174,6 +175,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick
type: "read-only",
style: {
textAlign: "left",
// width: "100px",
},
renderCell: (row) => {
return row.uomName;
@@ -185,6 +187,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick
type: "read-only",
style: {
textAlign: "right",
// width: "100px",
},
renderCell: (row) => {
return <ProdTimeColumn prodTimeInMinute={row.prodTimeInMinute} />
@@ -196,6 +199,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick
type: "read-only",
style: {
textAlign: "right",
// width: "100px",
},
// editable: true,
},


+ 94
- 10
src/components/PoDetail/PoDetail.tsx ファイルの表示

@@ -84,9 +84,15 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
const [rows, setRows] = useState<PurchaseOrderLine[]>(
purchaseOrder.pol || [],
);
const params = useSearchParams();
const searchParams = useSearchParams();
// const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status);

const removeParam = (paramToRemove: string) => {
const newParams = new URLSearchParams(searchParams.toString());
newParams.delete(paramToRemove);
window.history.replaceState({}, '', `${window.location.pathname}?${newParams}`);
};

const handleCompletePo = useCallback(async () => {
const checkRes = await checkPolAndCompletePo(purchaseOrder.id);
console.log(checkRes);
@@ -107,6 +113,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {

function Row(props: { row: PurchaseOrderLine }) {
const { row } = props;
const [firstReceiveQty, setFirstReceiveQty] = useState<number>()
const [secondReceiveQty, setSecondReceiveQty] = useState<number>()
const [open, setOpen] = useState(false);
const [processedQty, setProcessedQty] = useState(row.processed);
const [currStatus, setCurrStatus] = useState(row.status);
@@ -155,11 +163,30 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
<TableCell align="left">{row.price}</TableCell>
{/* <TableCell align="left">{row.expiryDate}</TableCell> */}
<TableCell align="left">{t(`${currStatus.toLowerCase()}`)}</TableCell>
{/* <TableCell align="left">
0
</TableCell>
<TableCell align="left">
<TextField
label="輸入數量"
type="text" // Use type="text" to allow validation in the change handler
variant="outlined"
value={secondReceiveQty}
// onChange={handleChange}
InputProps={{
inputProps: {
min: 0, // Optional: set a minimum value
step: 1 // Optional: set the step for the number input
}
}}
/>
</TableCell> */}
</TableRow>
<TableRow>
{/* <TableCell /> */}
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={12}>
<Collapse in={open} timeout="auto" unmountOnExit>
<Collapse in={true} timeout="auto" unmountOnExit>
{/* <Collapse in={open} timeout="auto" unmountOnExit> */}
<Table>
<TableBody>
<TableRow>
@@ -260,7 +287,21 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
}
}, [purchaseOrder.status, t, handleStartPo, handleCompletePo]);

console.log(window.innerWidth)
const FIRST_IN_FIELD = "firstInQty"
const SECOND_IN_FIELD = "secondInQty"

const renderFieldCondition = useCallback((field: "firstInQty" | "secondInQty"): boolean => {
switch (field) {
case FIRST_IN_FIELD:

return true;
case SECOND_IN_FIELD:
return true;
default:
return false; // Default case
}
}, []);

return (
<>
<Stack
@@ -277,18 +318,56 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
</Typography>
</Grid>
</Grid>
<Grid container xs={12} justifyContent="start">
<Grid item>
<Button
{false ? (<Grid container xs={12} justifyContent="start">
<Grid item xs={3}>
<TextField
label={t("dnNo")}
type="text" // Use type="text" to allow validation in the change handler
variant="outlined"
// value={secondReceiveQty}
// onChange={handleChange}
InputProps={{
inputProps: {
min: 0, // Optional: set a minimum value
step: 1 // Optional: set the step for the number input
}
}}
/>
</Grid>
<Grid item xs={3}>
<TextField
label={t("dnDate")}
type="text" // Use type="text" to allow validation in the change handler
variant="outlined"
defaultValue={"07/08/2025"}
// value={secondReceiveQty}
// onChange={handleChange}
InputProps={{
inputProps: {
min: 0, // Optional: set a minimum value
step: 1 // Optional: set the step for the number input
}
}}
/>
{/* <Button
onClick={buttonData.onClick}
disabled={buttonData.disabled}
color={buttonData.buttonColor as ButtonProps["color"]}
startIcon={buttonData.buttonIcon}
>
{buttonData.buttonText}
</Button>
</Button> */}
</Grid>
</Grid>
<Grid
item
xs={6}
display="flex"
justifyContent="end"
alignItems="end"
>
<Button onClick={onOpenScanner}>{t("Accept submit")}</Button>
</Grid>
</Grid>) : undefined}
<Grid container xs={12} justifyContent="space-between">
<Grid item xs={8}>
<Tabs
@@ -296,7 +375,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
onChange={handleTabChange}
variant="scrollable"
>
<Tab label={t("General")} iconPosition="end" />
{/* <Tab label={t("General")} iconPosition="end" /> */}
{/* <Tab label={t("Bind Storage")} iconPosition="end" /> */}
</Tabs>
</Grid>
@@ -315,7 +394,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
onClose={onCloseScanner}
warehouse={warehouse}
/>
<Button onClick={onOpenScanner}>{t("bind")}</Button>
{/* <Button onClick={onOpenScanner}>{t("Accept submit")}</Button> */}
{/* <Button onClick={onOpenScanner}>{t("bind")}</Button> */}
</Grid>
</Grid>
{/* tab 1 */}
@@ -336,6 +416,10 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
<TableCell align="left">{t("price")}</TableCell>
{/* <TableCell align="left">{t("expiryDate")}</TableCell> */}
<TableCell align="left">{t("status")}</TableCell>
{/* start == true && firstInQty == null ? no hide : hide*/}
{/* {renderFieldCondition(FIRST_IN_FIELD) ? <TableCell align="left">{t("receivedQty")}</TableCell> : undefined} */}
{/* start == true && firstInQty == null ? hide and disabled : no hide*/}
{/* {renderFieldCondition(SECOND_IN_FIELD) ? <TableCell align="left">{t("dnQty")}</TableCell> : undefined} */}
{/* <TableCell align="left">{"add icon button"}</TableCell> */}
</TableRow>
</TableHead>


+ 214
- 114
src/components/PoDetail/PoInputGrid.tsx ファイルの表示

@@ -57,6 +57,7 @@ import { fetchQcResult } from "@/app/api/qc/actions";
import PoQcStockInModal from "./PoQcStockInModal";
import DoDisturbIcon from "@mui/icons-material/DoDisturb";
import { useSession } from "next-auth/react";
import PoQcStockInModalVer2 from "./QcStockInModalVer2";

interface ResultWithId {
id: number;
@@ -255,6 +256,40 @@ function PoInputGrid({
},
[fetchQcDefaultValue, openQcModal],
);

const [newOpen, setNewOpen] = useState(false);
const closeNewModal = useCallback(() => {
setNewOpen(false);
}, []);
const openNewModal = useCallback(() => {
setNewOpen(true);
}, []);

const handleNewQC = useCallback(
(id: GridRowId, params: any) => async () => {
setBtnIsLoading(true);
setRowModesModel((prev) => ({
...prev,
[id]: { mode: GridRowModes.View },
}));
const qcResult = await fetchQcDefaultValue(id);
console.log(params.row);
console.log(qcResult);
setModalInfo({
...params.row,
qcResult: qcResult,
});
// set default values
setTimeout(() => {
// open qc modal
console.log("delayed");
openNewModal();
setBtnIsLoading(false);
}, 200);
},
[fetchQcDefaultValue, openNewModal],
);

const handleEscalation = useCallback(
(id: GridRowId, params: any) => () => {
// setBtnIsLoading(true);
@@ -373,20 +408,38 @@ function PoInputGrid({
{
field: "itemNo",
headerName: t("itemNo"),
width: 120,
width: 100,
// flex: 0.4,
},
{
field: "dnNo",
headerName: t("dnNo"),
width: 100,
renderCell: () => {
return <>DN0000001</>
}
// flex: 0.4,
},
{
field: "dnDate",
headerName: t("dnDate"),
width: 100,
renderCell: () => {
return <>07/08/2025</>
}
// flex: 0.4,
},
{
field: "itemName",
headerName: t("itemName"),
width: 120,
width: 100,
// flex: 0.6,
},
{
field: "acceptedQty",
headerName: t("acceptedQty"),
// flex: 0.5,
width: 120,
width: 100,
type: "number",
// editable: true,
// replace with tooltip + content
@@ -417,7 +470,7 @@ function PoInputGrid({
{
field: "status",
headerName: t("status"),
width: 120,
width: 70,
// flex: 0.5,
renderCell: (params) => {
return t(`${params.row.status}`);
@@ -426,12 +479,13 @@ function PoInputGrid({
{
field: "actions",
type: "actions",
headerName: `${t("start")} | ${t("qc")} | ${t("escalation")} | ${t(
"stock in",
)} | ${t("putaway")} | ${t("delete")}`,
// headerName: `${t("start")} | ${t("qc")} | ${t("escalation")} | ${t(
// "stock in",
// )} | ${t("putaway")} | ${t("delete")}`,
headerName: "動作",
// headerName: "start | qc | escalation | stock in | putaway | delete",
width: 300,
// flex: 1.5,
// flex: 2,
cellClassName: "actions",
getActions: (params) => {
// console.log(params.row.status);
@@ -440,130 +494,158 @@ function PoInputGrid({
// console.log(session?.user?.abilities?.includes("APPROVAL"));
return [
<GridActionsCellItem
icon={<PlayArrowIcon />}
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
}
// disabled={!(stockInLineStatusMap[status] === 0)}
// set _isNew to false after posting
// or check status
onClick={handleEscalation(params.row.id, params)}
onClick={handleNewQC(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"
icon={<Button variant="contained">{t("putawayBtn")}</Button>}
label="start"
sx={{
color: "primary.main",
// marginRight: 1,
}}
disabled={
stockInLineStatusMap[status] === 9 ||
stockInLineStatusMap[status] < 7
}
// disabled={!(stockInLineStatusMap[status] === 0)}
// set _isNew to false after posting
// or check status
onClick={handlePutAway(params.row.id, params)}
onClick={handleStart(params.row.id, params)}
color="inherit"
key="edit"
/>,
// <GridActionsCellItem
// icon={<QrCodeIcon />}
// 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] !== 8}
// disabled={
// stockInLineStatusMap[status] === 9 ||
// stockInLineStatusMap[status] < 7
// }
// // set _isNew to false after posting
// // or check status
// onClick={handleQrCode(params.row.id, params)}
// 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"
// />,
<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"
/>,
];
},
},
@@ -594,6 +676,7 @@ function PoInputGrid({
},
}));
}, [currQty, getRowId, itemDetail]);

const validation = useCallback(
(
newRow: GridRowModel<StockInLineRow>,
@@ -654,20 +737,22 @@ function PoInputGrid({
);

const footer = (
<Box display="flex" gap={2} alignItems="center">
<Button
disableRipple
variant="outlined"
startIcon={<Add />}
disabled={itemDetail.qty - currQty <= 0}
onClick={addRow}
size="small"
>
{t("Record pol")}
</Button>
</Box>
<>
{/* <Box display="flex" gap={2} alignItems="center">
<Button
disableRipple
variant="outlined"
startIcon={<Add />}
disabled={itemDetail.qty - currQty <= 0}
onClick={addRow}
size="small"
>
{t("Record pol")}
</Button>
</Box> */}
</>
);

return (
<>
<StyledDataGrid
@@ -715,6 +800,21 @@ function PoInputGrid({
footer: { child: footer },
}}
/>
{modalInfo !== undefined && (
<>
<PoQcStockInModalVer2
// setRows={setRows}
setEntries={setEntries}
setStockInLine={setStockInLine}
setItemDetail={setModalInfo}
qc={qc}
open={newOpen}
onClose={closeNewModal}
itemDetail={modalInfo}
/>
</>
)
}
{modalInfo !== undefined && (
<>
<PoQcStockInModal


+ 400
- 0
src/components/PoDetail/QCDatagrid.tsx ファイルの表示

@@ -0,0 +1,400 @@
"use client";
import {
Dispatch,
MutableRefObject,
SetStateAction,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import StyledDataGrid from "../StyledDataGrid";
import {
FooterPropsOverrides,
GridActionsCellItem,
GridCellParams,
GridColDef,
GridEventListener,
GridRowEditStopReasons,
GridRowId,
GridRowIdGetter,
GridRowModel,
GridRowModes,
GridRowModesModel,
GridRowSelectionModel,
GridToolbarContainer,
GridValidRowModel,
useGridApiRef,
} from "@mui/x-data-grid";
import { set, useFormContext } from "react-hook-form";
import SaveIcon from "@mui/icons-material/Save";
import DeleteIcon from "@mui/icons-material/Delete";
import CancelIcon from "@mui/icons-material/Cancel";
import { Add } from "@mui/icons-material";
import { Box, Button, Typography } from "@mui/material";
import { useTranslation } from "react-i18next";
import {
GridApiCommunity,
GridSlotsComponentsProps,
} from "@mui/x-data-grid/internals";
// T == CreatexxxInputs map of the form's fields
// V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc
// E == error
interface ResultWithId {
id: string | number;
}
// export type InputGridProps = {
// [key: string]: any
// }
interface DefaultResult<E> {
_isNew: boolean;
_error: E;
}

interface SelectionResult<E> {
active: boolean;
_isNew: boolean;
_error: E;
}
type Result<E> = DefaultResult<E> | SelectionResult<E>;

export type TableRow<V, E> = Partial<
V & {
isActive: boolean | undefined;
_isNew: boolean;
_error: E;
} & ResultWithId
>;

export interface InputDataGridProps<T, V, E> {
apiRef: MutableRefObject<GridApiCommunity>;
checkboxSelection: false | undefined;
_formKey: keyof T;
columns: GridColDef[];
validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E;
needAdd?: boolean;
}

export interface SelectionInputDataGridProps<T, V, E> {
// thinking how do
apiRef: MutableRefObject<GridApiCommunity>;
checkboxSelection: true;
_formKey: keyof T;
columns: GridColDef[];
validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E;
needAdd?: boolean;
}

export type Props<T, V, E> =
| InputDataGridProps<T, V, E>
| SelectionInputDataGridProps<T, V, E>;
export class ProcessRowUpdateError<T, E> extends Error {
public readonly row: T;
public readonly errors: E | undefined;
constructor(row: T, message?: string, errors?: E) {
super(message);
this.row = row;
this.errors = errors;

Object.setPrototypeOf(this, ProcessRowUpdateError.prototype);
}
}
// T == CreatexxxInputs map of the form's fields
// V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc
// E == error
function QcDatagrid<T, V, E>({
apiRef,
checkboxSelection = false,
_formKey,
columns,
validateRow,
needAdd,
}: Props<T, V, E>) {
const {
t,
// i18n: { language },
} = useTranslation("common");
const formKey = _formKey.toString();
const { setValue, getValues } = useFormContext();
const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
// const apiRef = useGridApiRef();
const getRowId = useCallback<GridRowIdGetter<TableRow<V, E>>>(
(row) => row.id! as number,
[],
);
const list: TableRow<V, E>[] = getValues(formKey);
// console.log(list)
const [rows, setRows] = useState<TableRow<V, E>[]>(() => {
const list: TableRow<V, E>[] = getValues(formKey);
return list && list.length > 0 ? list : [];
});
// const originalRows = list && list.length > 0 ? list : [];
const originalRows = useMemo(() => (
list && list.length > 0 ? list : []
), [list])
// const originalRowModel = originalRows.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel
const [rowSelectionModel, setRowSelectionModel] =
useState<GridRowSelectionModel>(() => {
// const rowModel = list.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel
const rowModel: GridRowSelectionModel = getValues(
`${formKey}_active`,
) as GridRowSelectionModel;
console.log(rowModel);
return rowModel;
});

const handleSave = useCallback(
(id: GridRowId) => () => {
setRowModesModel((prevRowModesModel) => ({
...prevRowModesModel,
[id]: { mode: GridRowModes.View },
}));
},
[],
);
const onProcessRowUpdateError = useCallback(
(updateError: ProcessRowUpdateError<T, E>) => {
const errors = updateError.errors;
const row = updateError.row;
console.log(errors);
apiRef.current.updateRows([{ ...row, _error: errors }]);
},
[apiRef],
);

const processRowUpdate = useCallback(
(
newRow: GridRowModel<TableRow<V, E>>,
originalRow: GridRowModel<TableRow<V, E>>,
) => {
/////////////////
// validation here
const errors = validateRow(newRow);
console.log(newRow);
if (errors) {
throw new ProcessRowUpdateError(
originalRow,
"validation error",
errors,
);
}
/////////////////
const { _isNew, _error, ...updatedRow } = newRow;
const rowToSave = {
...updatedRow,
} as TableRow<V, E>; /// test
console.log(rowToSave);
setRows((rw) =>
rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)),
);
return rowToSave;
},
[validateRow, getRowId],
);

const addRow = useCallback(() => {
const newEntry = { id: Date.now(), _isNew: true } as TableRow<V, E>;
setRows((prev) => [...prev, newEntry]);
setRowModesModel((model) => ({
...model,
[getRowId(newEntry)]: {
mode: GridRowModes.Edit,
// fieldToFocus: "team", /// test
},
}));
}, [getRowId]);

const reset = useCallback(() => {
setRowModesModel({});
setRows(originalRows);
}, [originalRows]);

const handleCancel = useCallback(
(id: GridRowId) => () => {
setRowModesModel((model) => ({
...model,
[id]: { mode: GridRowModes.View, ignoreModifications: true },
}));
const editedRow = rows.find((row) => getRowId(row) === id);
if (editedRow?._isNew) {
setRows((rw) => rw.filter((r) => getRowId(r) !== id));
} else {
setRows((rw) =>
rw.map((r) => (getRowId(r) === id ? { ...r, _error: undefined } : r)),
);
}
},
[rows, getRowId],
);

const handleDelete = useCallback(
(id: GridRowId) => () => {
setRows((prevRows) => prevRows.filter((row) => getRowId(row) !== id));
},
[getRowId],
);

const _columns = useMemo<GridColDef[]>(
() => [
...columns,
{
field: "actions",
type: "actions",
headerName: "",
flex: 0.5,
cellClassName: "actions",
getActions: ({ id }: { id: GridRowId }) => {
const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit;
if (isInEditMode) {
return [
<GridActionsCellItem
icon={<SaveIcon />}
label="Save"
key="edit"
sx={{
color: "primary.main",
}}
onClick={handleSave(id)}
/>,
<GridActionsCellItem
icon={<CancelIcon />}
label="Cancel"
key="edit"
onClick={handleCancel(id)}
/>,
];
}
return [
<GridActionsCellItem
icon={<DeleteIcon />}
label="Delete"
sx={{
color: "error.main",
}}
onClick={handleDelete(id)}
color="inherit"
key="edit"
/>,
];
},
},
],
[columns, rowModesModel, handleSave, handleCancel, handleDelete],
);
// sync useForm
useEffect(() => {
// console.log(formKey)
// console.log(rows)
setValue(formKey, rows);
}, [formKey, rows, setValue]);

const footer = (
<Box display="flex" gap={2} alignItems="center">
<Button
disableRipple
variant="outlined"
startIcon={<Add />}
onClick={addRow}
size="small"
>
{t("Add Record")}
</Button>
<Button
disableRipple
variant="outlined"
startIcon={<Add />}
onClick={reset}
size="small"
>
{t("Clean Record")}
</Button>
</Box>
);
// const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => {
// if (params.reason === GridRowEditStopReasons.rowFocusOut) {
// event.defaultMuiPrevented = true;
// }
// };

return (
<StyledDataGrid
// {...props}
// getRowId={getRowId as GridRowIdGetter<GridValidRowModel>}
// checkbox selection
checkboxSelection={checkboxSelection}
disableRowSelectionOnClick={checkboxSelection}
onRowSelectionModelChange={(newRowSelectionModel) => {
if (checkboxSelection) {
setRowSelectionModel(newRowSelectionModel);
setValue("qcChecks_active", newRowSelectionModel);
}
}}
rowSelectionModel={rowSelectionModel}
apiRef={apiRef}
rows={rows}
columns={!checkboxSelection ? _columns : columns}
editMode="row"
autoHeight
sx={{
"--DataGrid-overlayHeight": "100px",
".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
border: "1px solid",
borderColor: "error.main",
},
".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": {
border: "1px solid",
borderColor: "warning.main",
},
}}
disableColumnMenu
processRowUpdate={processRowUpdate as any}
// onRowEditStop={handleRowEditStop}
rowModesModel={rowModesModel}
onRowModesModelChange={setRowModesModel}
onProcessRowUpdateError={onProcessRowUpdateError}
getCellClassName={(params: GridCellParams<TableRow<T, E>>) => {
let classname = "";
if (params.row._error) {
classname = "hasError";
}
return classname;
}}
slots={
!checkboxSelection
? {
footer: FooterToolbar,
noRowsOverlay: NoRowsOverlay,
}
: undefined
}
slotProps={
!checkboxSelection && Boolean(needAdd)
? {
footer: { child: footer },
}
: undefined
// slotProps={renderFooter ? {
// footer: { child: footer },
// }: undefined
}
/>
);
}
const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => {
return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>;
};
const NoRowsOverlay: React.FC = () => {
const { t } = useTranslation("home");
return (
<Box
display="flex"
justifyContent="center"
alignItems="center"
height="100%"
>
<Typography variant="caption">{t("Add some entries!")}</Typography>
</Box>
);
};
export default QcDatagrid;

+ 242
- 0
src/components/PoDetail/QcFormVer2.tsx ファイルの表示

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

import { PurchaseQcResult, PurchaseQCInput } from "@/app/api/po/actions";
import {
Box,
Card,
CardContent,
Grid,
Stack,
Tab,
Tabs,
TabsProps,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useEffect, useMemo, useState } from "react";
import {
GridColDef,
GridRowIdGetter,
GridRowModel,
useGridApiContext,
GridRenderCellParams,
GridRenderEditCellParams,
useGridApiRef,
} from "@mui/x-data-grid";
import InputDataGrid from "../InputDataGrid";
import { TableRow } from "../InputDataGrid/InputDataGrid";
import TwoLineCell from "./TwoLineCell";
import QcSelect from "./QcSelect";
import { GridEditInputCell } from "@mui/x-data-grid";
import { StockInLine } from "@/app/api/po";
import { stockInLineStatusMap } from "@/app/utils/formatUtil";
import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions";
import { QcItemWithChecks } from "@/app/api/qc";
import axios from "@/app/(main)/axios/axiosInstance";
import { NEXT_PUBLIC_API_URL } from "@/config/api";
import axiosInstance from "@/app/(main)/axios/axiosInstance";

interface Props {
itemDetail: StockInLine;
qc: QcItemWithChecks[];
disabled: boolean;
}
type EntryError =
| {
[field in keyof PurchaseQcResult]?: string;
}
| undefined;

type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>;
// fetchQcItemCheck
const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => {
const { t } = useTranslation("purchaseOrder");
const apiRef = useGridApiRef();
const {
register,
formState: { errors, defaultValues, touchedFields },
watch,
control,
setValue,
getValues,
reset,
resetField,
setError,
clearErrors,
} = useFormContext<PurchaseQCInput>();
console.log(itemDetail);
console.log(defaultValues);
const [tabIndex, setTabIndex] = useState(0);

const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
setTabIndex(newValue);
},
[],
);

//// validate form
const accQty = watch("acceptedQty");
const validateForm = useCallback(() => {
console.log(accQty);
if (accQty > itemDetail.acceptedQty) {
setError("acceptedQty", {
message: `${t("acceptedQty must not greater than")} ${
itemDetail.acceptedQty
}`,
type: "required",
});
}
if (accQty < 1) {
setError("acceptedQty", {
message: t("minimal value is 1"),
type: "required",
});
}
if (isNaN(accQty)) {
setError("acceptedQty", {
message: t("value must be a number"),
type: "required",
});
}
}, [accQty]);

useEffect(() => {
clearErrors();
validateForm();
}, [clearErrors, validateForm]);

const columns = useMemo<GridColDef[]>(
() => [
{
field: "qcItemId",
headerName: t("qc Check"),
flex: 1,
editable: !disabled,
valueFormatter(params) {
const row = params.id ? params.api.getRow<PoQcRow>(params.id) : null;
if (!row) {
return null;
}
const Qc = qc.find((q) => q.id === row.qcItemId);
return Qc ? `${Qc.code} - ${Qc.name}` : t("Please select QC");
},
renderCell(params: GridRenderCellParams<PoQcRow, number>) {
console.log(params.value);
return <TwoLineCell>{params.formattedValue}</TwoLineCell>;
},
renderEditCell(params: GridRenderEditCellParams<PoQcRow, number>) {
const errorMessage =
params.row._error?.[params.field as keyof PurchaseQcResult];
console.log(errorMessage);
const content = (
<QcSelect
allQcs={qc}
value={params.row.qcItemId}
onQcSelect={async (qcItemId) => {
await params.api.setEditCellValue({
id: params.id,
field: "qcItemId",
value: qcItemId,
});
// await params.api.setEditCellValue({
// id: params.id,
// field: "type",
// value: "determine1",
// });
}}
/>
);
return errorMessage ? (
<Tooltip title={errorMessage}>
<Box width="100%">{content}</Box>
</Tooltip>
) : (
content
);
},
},
{
field: "failQty",
headerName: t("failQty"),
flex: 1,
editable: !disabled,
type: "number",
renderEditCell(params: GridRenderEditCellParams<PoQcRow>) {
// const recordQty = params.row.qty
// if (recordQty !== undefined) {
// setUnrecordQty((prev) => prev - recordQty)
// }
const errorMessage =
params.row._error?.[params.field as keyof PurchaseQcResult];
const content = <GridEditInputCell {...params} />;
return errorMessage ? (
<Tooltip title={t(errorMessage)}>
<Box width="100%">{content}</Box>
</Tooltip>
) : (
content
);
},
},
],
[qc],
);
/// validate datagrid
const validation = useCallback(
(newRow: GridRowModel<PoQcRow>): EntryError => {
const error: EntryError = {};
const { qcItemId, failQty } = newRow;
if (!qcItemId || qcItemId <= 0) {
error["qcItemId"] = t("select qc");
}
if (!failQty || failQty <= 0) {
error["failQty"] = t("enter a failQty");
}
if (failQty && failQty > itemDetail.acceptedQty) {
error["failQty"] = t("qty too big");
}
return Object.keys(error).length > 0 ? error : undefined;
},
[],
);

useEffect(() => {
console.log(itemDetail);
const status = "receiving";
// switch (itemDetail.status) {
// case 'pending':
// status = "receiving"
// break;
// }
setValue("status", status);
}, [itemDetail]);

return (
<>
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
spacing={2}
sx={{ mt: 0.5 }}
>
<Tabs
value={tabIndex}
onChange={handleTabChange}
variant="scrollable"
>
<Tab label={t("QC Info")} iconPosition="end" />
<Tab label={t("Escalation History")} iconPosition="end" />
</Tabs>
</Grid>
</Grid>
</>
);
};
export default QcFormVer2;

+ 149
- 0
src/components/PoDetail/QcStockInModalVer2.tsx ファイルの表示

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

import { StockInLine } from "@/app/api/po";
import { ModalFormInput, PurchaseQcResult } from "@/app/api/po/actions";
import { QcItemWithChecks } from "@/app/api/qc";
import { Box, Button, Grid, Modal, ModalProps, Stack, Typography } from "@mui/material";
import { Dispatch, SetStateAction, useCallback, useState } from "react";
import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
import { StockInLineRow } from "./PoInputGrid";
import { useTranslation } from "react-i18next";
import StockInForm from "./StockInForm";
import StockInFormVer2 from "./StockInFormVer2";
import QcFormVer2 from "./QcFormVer2";

const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
overflowY: "scroll",
bgcolor: "background.paper",
pt: 5,
px: 5,
pb: 10,
display: "block",
width: { xs: "60%", sm: "60%", md: "60%" },
};

interface CommonProps extends Omit<ModalProps, "children"> {
// setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>;
setStockInLine?: Dispatch<SetStateAction<StockInLine[]>>;
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };
setItemDetail: Dispatch<
SetStateAction<
| (StockInLine & {
warehouseId?: number;
})
| undefined
>
>;
qc?: QcItemWithChecks[];
warehouse?: any[];
// type: "qc" | "stockIn" | "escalation" | "putaway" | "reject";
}

interface Props extends CommonProps{
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };

}
const PoQcStockInModalVer2: React.FC<Props> = ({
// type,
// setRows,
setEntries,
setStockInLine,
open,
onClose,
itemDetail,
setItemDetail,
qc,
warehouse,
}) => {
const {
t,
i18n: { language },
} = useTranslation("purchaseOrder");
const formProps = useForm<ModalFormInput>({
defaultValues: {
...itemDetail,
// receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT),
// warehouseId: itemDetail.defaultWarehouseId || 0
},
});
const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
(...args) => {
onClose?.(...args);
// reset();
},
[onClose],
);

const [submissionType, setSubmissionType] = useState<"stockIn" | "qc" | "escalate" | undefined>(undefined)
const onSubmit = useCallback<SubmitHandler<ModalFormInput>>(
async (data, event) => {
console.log(event!.nativeEvent)
// divide 3 section for this submition
// switch (submissionType) {
// submit stock in data
// submit qc data
// submit putaway
// }

}, [submissionType])

return (
<>
<FormProvider {...formProps}>
<Modal open={open} onClose={closeHandler} sx={{ overflowY: "scroll" }}>
<Box
sx={style}
component="form"
onSubmit={formProps.handleSubmit(onSubmit)}
>
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("qc processing")}
</Typography>
</Grid>
<Grid item xs={12}>
<StockInFormVer2
itemDetail={itemDetail}
disabled={false}
/>
</Grid>
</Grid>
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
id="stockIn"
type="button"
variant="contained"
color="primary"
>
{t("submitStockIn")}
</Button>
</Stack>
<Grid container justifyContent="flex-start" alignItems="flex-start">
<QcFormVer2
qc={qc!}
itemDetail={itemDetail}
disabled={false}
/>
</Grid>
<Button
id="qc"
type="button"
variant="contained"
color="secondary"
>
Submit QC
</Button>
</Box>
</Modal>
</FormProvider>
</>
)
}
export default PoQcStockInModalVer2

+ 323
- 0
src/components/PoDetail/StockInFormVer2.tsx ファイルの表示

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

import {
PurchaseQcResult,
PurchaseQCInput,
StockInInput,
} from "@/app/api/po/actions";
import {
Box,
Card,
CardContent,
Grid,
Stack,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useEffect, useMemo } from "react";
import {
GridColDef,
GridRowIdGetter,
GridRowModel,
useGridApiContext,
GridRenderCellParams,
GridRenderEditCellParams,
useGridApiRef,
} from "@mui/x-data-grid";
import InputDataGrid from "../InputDataGrid";
import { TableRow } from "../InputDataGrid/InputDataGrid";
import TwoLineCell from "./TwoLineCell";
import QcSelect from "./QcSelect";
import { QcItemWithChecks } from "@/app/api/qc";
import { GridEditInputCell } from "@mui/x-data-grid";
import { StockInLine } from "@/app/api/po";
import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import dayjs from "dayjs";
// change PurchaseQcResult to stock in entry props
interface Props {
itemDetail: StockInLine;
// qc: QcItemWithChecks[];
disabled: boolean;
}
type EntryError =
| {
[field in keyof StockInInput]?: string;
}
| undefined;

// type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>;

const StockInFormVer2: React.FC<Props> = ({
// qc,
itemDetail,
disabled,
}) => {
const {
t,
i18n: { language },
} = useTranslation("purchaseOrder");
const apiRef = useGridApiRef();
const {
register,
formState: { errors, defaultValues, touchedFields },
watch,
control,
setValue,
getValues,
reset,
resetField,
setError,
clearErrors,
} = useFormContext<StockInInput>();
console.log(itemDetail);

useEffect(() => {
console.log("triggered");
// receiptDate default tdy
setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT));
setValue("status", "received");
}, [setValue]);

useEffect(() => {
console.log(errors);
}, [errors]);

const productionDate = watch("productionDate");
const expiryDate = watch("expiryDate");

useEffect(() => {
console.log(productionDate);
console.log(expiryDate);
if (expiryDate) clearErrors();
if (productionDate) clearErrors();
}, [productionDate, expiryDate, clearErrors]);

console.log(itemDetail)

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
{/* <Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Stock In Detail")}
</Typography>
</Grid> */}
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
spacing={2}
sx={{ mt: 0.5 }}
>
<Grid item xs={6}>
<TextField
label={t("dnNo")}
fullWidth
{...register("dnNo", {
// required: "productLotNo required!",
})}
disabled={true}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("itemName")}
fullWidth
{...register("itemName", {
// required: "productLotNo required!",
})}
disabled={true}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("PO No.")}
fullWidth
{...register("poCode", {
// required: "productLotNo required!",
})}
disabled={true}
// error={Boolean(errors.productLotNo)}
// helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={6}>
<Controller
control={control}
name="receiptDate"
rules={{ required: true }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("receiptDate")}
value={dayjs(watch("receiptDate"))}
disabled={true}
onChange={(date) => {
if (!date) return;
// setValue("receiptDate", date.format(INPUT_DATE_FORMAT));
field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.receiptDate?.message),
helperText: errors.receiptDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Grid item xs={6}>
<Controller
control={control}
name="productionDate"
// rules={{ required: !Boolean(expiryDate) }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("productionDate")}
value={productionDate ? dayjs(productionDate) : undefined}
disabled={disabled}
onChange={(date) => {
if (!date) return;
setValue(
"productionDate",
date.format(INPUT_DATE_FORMAT),
);
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.productionDate?.message),
helperText: errors.productionDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("productLotNo")}
fullWidth
{...register("productLotNo", {
// required: "productLotNo required!",
})}
disabled={disabled}
error={Boolean(errors.productLotNo)}
helperText={errors.productLotNo?.message}
/>
</Grid>
<Grid item xs={6}>
<Controller
control={control}
name="expiryDate"
// rules={{ required: !Boolean(productionDate) }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("expiryDate")}
value={expiryDate ? dayjs(expiryDate) : undefined}
disabled={disabled}
onChange={(date) => {
console.log(date);
if (!date) return;
console.log(date.format(INPUT_DATE_FORMAT));
setValue("expiryDate", date.format(INPUT_DATE_FORMAT));
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.expiryDate?.message),
helperText: errors.expiryDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Grid item xs={6}>
<TextField
label={t("acceptedQty")}
fullWidth
{...register("acceptedQty", {
required: "acceptedQty required!",
})}
disabled={disabled}
error={Boolean(errors.acceptedQty)}
helperText={errors.acceptedQty?.message}
/>
</Grid>
{/* <Grid item xs={4}>
<TextField
label={t("acceptedWeight")}
fullWidth
// {...register("acceptedWeight", {
// required: "acceptedWeight required!",
// })}
disabled={disabled}
error={Boolean(errors.acceptedWeight)}
helperText={errors.acceptedWeight?.message}
/>
</Grid> */}
</Grid>
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
spacing={2}
sx={{ mt: 0.5 }}
>
{/* <Grid item xs={12}>
<InputDataGrid<PurchaseQCInput, PurchaseQcResult, EntryError>
apiRef={apiRef}
checkboxSelection={false}
_formKey={"qcCheck"}
columns={columns}
validateRow={validationTest}
/>
</Grid> */}
</Grid>
</Grid>
);
};
export default StockInFormVer2;

+ 23
- 0
src/components/PoDetail/dummyQcTemplate.tsx ファイルの表示

@@ -0,0 +1,23 @@
const dummyQCData = [
{
id: 1,
qcItem: "目測",
isPassed: undefined,
failedQty: undefined,
remarks: undefined,
},
{
id: 2,
qcItem: "目測2",
isPassed: undefined,
failedQty: undefined,
remarks: undefined,
},
{
id: 3,
qcItem: "目測3",
isPassed: undefined,
failedQty: undefined,
remarks: undefined,
},
]

+ 1
- 1
src/components/RoughSchedule/RoughSchedileSearchView.tsx ファイルの表示

@@ -253,7 +253,7 @@ const RSOverview: React.FC<Props> = ({ type, defaultInputs }) => {
// setFilterObj({});
// setTempSelectedValue({});
refetchData(defaultInputs, "reset");
}, []);
}, [defaultInputs, refetchData]);

const testRoughScheduleClick = useCallback(async () => {
try {


+ 3
- 0
src/components/StyledDataGrid/StyledDataGrid.tsx ファイルの表示

@@ -17,6 +17,9 @@ const StyledDataGrid = styled(DataGrid)(({ theme }) => ({
"& .MuiDataGrid-columnSeparator": {
color: theme.palette.primary.main,
},
"& .MuiDataGrid-row:nth-of-type(even)": {
backgroundColor: theme.palette.grey[200], // Light grey for even rows
},
}));

export default StyledDataGrid;

+ 13
- 0
src/i18n/zh/purchaseOrder.json ファイルの表示

@@ -82,6 +82,19 @@
"Po Code": "採購訂單編號",
"No Warehouse": "沒有倉庫",
"Please scan warehouse qr code.": "請掃描倉庫 QR 碼。",
"receivedQty": "已來貨數量",
"dnQty": "送貨單數量",

"Accept submit": "接受來貨",
"qc processing": "處理來貨及品檢",
"putawayBtn": "上架",
"dnNo": "送貨單編號",
"dnDate": "送貨單日期",

"submitStockIn": "更新來貨資料",
"QC Info": "品檢資料",
"Escalation History": "品檢資料",

"Reject": "拒絕",
"submit": "提交",


読み込み中…
キャンセル
保存