# Conflicts: # src/components/PoDetail/PoDetail.tsxmaster
@@ -1,3 +1,4 @@ | |||
import { fetchQcCategoryCombo } from "@/app/api/settings/qcCategory"; | |||
import { SearchParams } from "@/app/utils/fetchUtil"; | |||
import { TypeEnum } from "@/app/utils/typeEnum"; | |||
import CreateProductMaterial from "@/components/CreateItem"; | |||
@@ -17,6 +18,9 @@ const productSetting: React.FC<Props> = async ({ searchParams }) => { | |||
if (!id) { | |||
notFound(); | |||
} | |||
fetchQcCategoryCombo() | |||
return ( | |||
<> | |||
{/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||
@@ -6,6 +6,7 @@ import { serverFetchJson } from "../../utils/fetchUtil"; | |||
import { BASE_API_URL } from "../../../config/api"; | |||
import { Uom } from "../settings/uom"; | |||
import { RecordsRes } from "../utils"; | |||
import { IQCItems } from "@/components/DashboardPage/QC/SupervisorQcApproval"; | |||
export interface PoResult { | |||
id: number; | |||
@@ -81,3 +82,9 @@ export const fetchPoWithStockInLines = cache(async (id: number) => { | |||
next: { tags: ["po"] }, | |||
}); | |||
}); | |||
export const fetchIqcLogByUser = cache(async () => { | |||
return serverFetchJson<IQCItems[]>(`${BASE_API_URL}/supervisionApprovalLog/stock-in`, { | |||
next: { tags: ["qcLog"] }, | |||
}); | |||
}); |
@@ -35,6 +35,7 @@ export type CreateItemInputs = { | |||
type: string; | |||
qcChecks: QcChecksInputs[]; | |||
qcChecks_active: number[]; | |||
qcCategoryId: number | undefined; | |||
}; | |||
export const saveItem = async (data: CreateItemInputs) => { | |||
@@ -4,6 +4,7 @@ import "server-only"; | |||
// import { BASE_API_URL } from "@/config/api"; | |||
import { serverFetchJson } from "../../../utils/fetchUtil"; | |||
import { BASE_API_URL } from "../../../../config/api"; | |||
import { QcCategoryResult } from "../qcCategory"; | |||
// import { TypeInputs, UomInputs, WeightUnitInputs } from "./actions"; | |||
@@ -37,6 +38,7 @@ export type ItemsResult = { | |||
action?: any; | |||
fgName?: string; | |||
excludeDate?: string; | |||
qcCategory?: QcCategoryResult; | |||
}; | |||
export type Result = { | |||
@@ -9,6 +9,12 @@ export interface QcCategoryResult { | |||
name: string; | |||
} | |||
export interface QcCategoryCombo { | |||
id: number; | |||
value: number; | |||
label: string; | |||
} | |||
export const preloadQcCategory = () => { | |||
fetchQcCategories(); | |||
}; | |||
@@ -18,3 +24,9 @@ export const fetchQcCategories = cache(async () => { | |||
next: { tags: ["qcCategories"] }, | |||
}); | |||
}); | |||
export const fetchQcCategoryCombo = cache(async () => { | |||
return serverFetchJson<QcCategoryCombo[]>(`${BASE_API_URL}/qcCategories/combo`, { | |||
next: { tags: ["qcCategoryCombo"] }, | |||
}); | |||
}); |
@@ -0,0 +1,20 @@ | |||
"server only" | |||
import { BASE_API_URL } from '@/config/api'; | |||
import { serverFetchJson } from '@/app/utils/fetchUtil'; | |||
import { cache } from "react"; | |||
export interface ShopCombo { | |||
id: number; | |||
value: number; // id | |||
label: string; | |||
} | |||
export const fetchSupplierCombo = cache(async() => { | |||
return serverFetchJson<ShopCombo[]>(`${BASE_API_URL}/shop/combo/supplier`, { | |||
method: "GET", | |||
headers: { "Content-Type": "application/json"}, | |||
next: { | |||
tags: ["supplierCombo"] | |||
} | |||
}) | |||
}) |
@@ -29,12 +29,14 @@ import QcDetails from "./QcDetails"; | |||
import { ItemQc } from "@/app/api/settings/item"; | |||
import { saveItemQcChecks } from "@/app/api/settings/qcCheck/actions"; | |||
import { useGridApiRef } from "@mui/x-data-grid"; | |||
import { QcCategoryCombo } from "@/app/api/settings/qcCategory"; | |||
type Props = { | |||
isEditMode: boolean; | |||
// type: TypeEnum; | |||
defaultValues: Partial<CreateItemInputs> | undefined; | |||
qcChecks: ItemQc[]; | |||
qcCategoryCombo: QcCategoryCombo[] | |||
}; | |||
const CreateItem: React.FC<Props> = ({ | |||
@@ -42,6 +44,7 @@ const CreateItem: React.FC<Props> = ({ | |||
// type, | |||
defaultValues, | |||
qcChecks, | |||
qcCategoryCombo, | |||
}) => { | |||
// console.log(type) | |||
const apiRef = useGridApiRef(); | |||
@@ -192,7 +195,7 @@ const CreateItem: React.FC<Props> = ({ | |||
{serverError} | |||
</Typography> | |||
)} | |||
{tabIndex === 0 && <ProductDetails isEditMode={isEditMode} />} | |||
{tabIndex === 0 && <ProductDetails isEditMode={isEditMode} qcCategoryCombo={qcCategoryCombo}/>} | |||
{tabIndex === 1 && <QcDetails apiRef={apiRef} />} | |||
{/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */} | |||
{/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */} | |||
@@ -5,6 +5,7 @@ import { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||
import { notFound } from "next/navigation"; | |||
import { fetchItem } from "@/app/api/settings/item"; | |||
import { fetchQcItems } from "@/app/api/settings/qcItem"; | |||
import { fetchQcCategoryCombo } from "@/app/api/settings/qcCategory"; | |||
interface SubComponents { | |||
Loading: typeof CreateItemLoading; | |||
} | |||
@@ -37,14 +38,18 @@ const CreateItemWrapper: React.FC<Props> & SubComponents = async ({ id }) => { | |||
maxQty: item?.maxQty, | |||
qcChecks: qcChecks, | |||
qcChecks_active: activeRows, | |||
qcCategoryId: item.qcCategory?.id | |||
}; | |||
} | |||
const qcCategoryCombo = await fetchQcCategoryCombo(); | |||
return ( | |||
<CreateItem | |||
isEditMode={Boolean(id)} | |||
defaultValues={defaultValues} | |||
qcChecks={qcChecks || []} | |||
qcCategoryCombo={qcCategoryCombo} | |||
/> | |||
); | |||
}; | |||
@@ -1,5 +1,6 @@ | |||
"use client"; | |||
import { | |||
Autocomplete, | |||
Box, | |||
Button, | |||
Card, | |||
@@ -10,11 +11,11 @@ import { | |||
Typography, | |||
} from "@mui/material"; | |||
import { Check, Close, EditNote } from "@mui/icons-material"; | |||
import { useFormContext } from "react-hook-form"; | |||
import { Controller, useFormContext } from "react-hook-form"; | |||
import { useTranslation } from "react-i18next"; | |||
import InputDataGrid from "../InputDataGrid"; | |||
import { useCallback, useMemo, useState } from "react"; | |||
import { SyntheticEvent, useCallback, useMemo, useState } from "react"; | |||
import { GridColDef, GridRowModel } from "@mui/x-data-grid"; | |||
import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; | |||
import { TypeEnum } from "@/app/utils/typeEnum"; | |||
@@ -22,6 +23,7 @@ import { NumberInputProps } from "./NumberInputProps"; | |||
import { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||
import { RestartAlt } from "@mui/icons-material"; | |||
import { ItemQc } from "@/app/api/settings/item"; | |||
import { QcCategoryCombo } from "@/app/api/settings/qcCategory"; | |||
type Props = { | |||
// isEditMode: boolean; | |||
// type: TypeEnum; | |||
@@ -29,9 +31,10 @@ type Props = { | |||
// type: TypeEnum; | |||
defaultValues?: Partial<CreateItemInputs> | undefined; | |||
qcChecks?: ItemQc[]; | |||
qcCategoryCombo: QcCategoryCombo[]; | |||
}; | |||
const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||
const ProductDetails: React.FC<Props> = ({ isEditMode, qcCategoryCombo }) => { | |||
const { | |||
t, | |||
i18n: { language }, | |||
@@ -104,6 +107,11 @@ const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||
// router.replace(`/settings/product`); | |||
console.log("cancel"); | |||
}; | |||
const handleAutoCompleteChange = useCallback((event: SyntheticEvent<Element, Event>, value: QcCategoryCombo, onChange: (...event: any[]) => void) => { | |||
onChange(value.id) | |||
}, []) | |||
return ( | |||
<Card sx={{ display: "block" }}> | |||
<CardContent component={Stack} spacing={4}> | |||
@@ -202,7 +210,31 @@ const ProductDetails: React.FC<Props> = ({ isEditMode }) => { | |||
helperText={errors.maxQty?.message} | |||
/> | |||
</Grid> | |||
<Grid item xs={0}> | |||
<Grid item xs={6}> | |||
<Controller | |||
control={control} | |||
name="qcCategoryId" | |||
render={({ field }) => ( | |||
<Autocomplete | |||
disableClearable | |||
options={qcCategoryCombo} | |||
defaultValue={qcCategoryCombo.find(qc => qc.id === field.value)} | |||
onChange={(event, value) => { | |||
handleAutoCompleteChange(event, value, field.onChange) | |||
}} | |||
onBlur={field.onBlur} | |||
renderInput={(params) => ( | |||
<TextField | |||
{...params} | |||
variant="outlined" | |||
label={t("Qc Category")} | |||
/> | |||
)} | |||
/> | |||
)} | |||
/> | |||
</Grid> | |||
<Grid item xs={12}> | |||
<Stack | |||
direction="row" | |||
justifyContent="flex-end" | |||
@@ -14,15 +14,27 @@ import ApplicationCompletionChart from "./chart/ApplicationCompletionChart"; | |||
import OrderCompletionChart from "./chart/OrderCompletionChart"; | |||
import DashboardBox from "./Dashboardbox"; | |||
import CollapsibleCard from "./CollapsibleCard"; | |||
type Props = {}; | |||
import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval"; | |||
type Props = { | |||
iqc: IQCItems[] | |||
}; | |||
const DashboardPage: React.FC<Props> = ({}) => { | |||
const DashboardPage: React.FC<Props> = ({ | |||
iqc | |||
}) => { | |||
const { t } = useTranslation("dashboard"); | |||
const router = useRouter(); | |||
return ( | |||
<ThemeProvider theme={theme}> | |||
<Grid container spacing={2}> | |||
<Grid item xs={12}> | |||
<CollapsibleCard title={t("stock in escalation list")}> | |||
<CardContent> | |||
<SupervisorQcApproval items={iqc || []}/> | |||
</CardContent> | |||
</CollapsibleCard> | |||
</Grid> | |||
<Grid item xs={12}> | |||
<CollapsibleCard title={t("Progress chart")}> | |||
<CardContent> | |||
@@ -4,6 +4,7 @@ import DashboardPage from "./DashboardPage"; | |||
import { Typography } from "@mui/material"; | |||
import { I18nProvider, getServerI18n } from "@/i18n"; | |||
import DashboardLoading from "./DashboardLoading"; | |||
import { fetchIqcLogByUser } from "@/app/api/dashboard"; | |||
// export type SessionWithAbilities = { | |||
// abilities: string[] | |||
@@ -22,11 +23,16 @@ const DashboardWrapper: React.FC<Props> & SubComponents = async ({ | |||
}) => { | |||
const { t } = await getServerI18n("dashboard"); | |||
// const session: SessionWithAbilities = await getServerSession(authOptions) | |||
const [iqcLog] = await Promise.all([ | |||
fetchIqcLogByUser() | |||
]) | |||
console.log(iqcLog) | |||
return ( | |||
<> | |||
<Typography variant="h4">{t("Dashboard")}</Typography> | |||
<DashboardPage | |||
iqc={iqcLog} | |||
// abilities={session ? session?.abilities : []} | |||
/> | |||
</> | |||
@@ -0,0 +1,93 @@ | |||
"use client" | |||
import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material"; | |||
import { useRouter } from "next/navigation"; | |||
import { useCallback, useState } from "react"; | |||
import { usePathname } from "next/navigation"; | |||
import { useTranslation } from "react-i18next"; | |||
export type IQCItems = { | |||
id: number; | |||
poId: number; | |||
polId: number; | |||
stockInLineId: number; | |||
poCode: string | |||
itemName: string | |||
escalationLevel: string | |||
reason: string | |||
}; | |||
type Props = { | |||
items: IQCItems[]; | |||
}; | |||
const SupervisorQcApproval: React.FC<Props> = ({ | |||
items | |||
}) => { | |||
const { t } = useTranslation("dashboard"); | |||
const CARD_HEADER = t("stock in escalation list") | |||
const pathname = usePathname(); | |||
const router = useRouter(); | |||
const [selectedId, setSelectedId] = useState<number | null>(null); | |||
const navigateTo = useCallback( | |||
(item: IQCItems) => { | |||
setSelectedId(item.id); | |||
console.log(pathname) | |||
router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | |||
}, | |||
[router, pathname] | |||
); | |||
const handleKeyDown = useCallback( | |||
(e: React.KeyboardEvent, item: IQCItems) => { | |||
if (e.key === 'Enter' || e.key === ' ') { | |||
e.preventDefault(); | |||
navigateTo(item); | |||
} | |||
}, | |||
[navigateTo] | |||
); | |||
return ( | |||
<TableContainer component={Paper}> | |||
<Table aria-label="Two column navigable table" size="small"> | |||
<TableHead> | |||
<TableRow> | |||
<TableCell>{t("purchase order code")}</TableCell> | |||
<TableCell>{t("item name")}</TableCell> | |||
<TableCell>{t("escalation level")}</TableCell> | |||
<TableCell>{t("reason")}</TableCell> | |||
</TableRow> | |||
</TableHead> | |||
<TableBody> | |||
{items.map((item) => { | |||
const selected = selectedId === item.id; | |||
return ( | |||
<TableRow | |||
key={item.id} | |||
hover | |||
selected={selected} | |||
onClick={() => navigateTo(item)} | |||
// onKeyDown={(e) => handleKeyDown(e, item)} | |||
tabIndex={0} | |||
sx={{ cursor: 'pointer' }} | |||
// aria-label={`${item.name}, ${item.detail}`} | |||
> | |||
<TableCell component="th" scope="row"> | |||
{item.poCode} | |||
</TableCell> | |||
<TableCell>{item.itemName}</TableCell> | |||
<TableCell>{item.escalationLevel}</TableCell> | |||
<TableCell>{item.reason}</TableCell> | |||
</TableRow> | |||
); | |||
})} | |||
</TableBody> | |||
</Table> | |||
</TableContainer> | |||
); | |||
}; | |||
export default SupervisorQcApproval; |
@@ -82,23 +82,23 @@ const InventoryLotLineTable: React.FC<Props> = ({ inventoryLotLines, pagingContr | |||
}, | |||
{ | |||
name: "uom", | |||
label: t("Sales UoM"), | |||
align: "left", | |||
headerAlign: "left", | |||
}, | |||
{ | |||
name: "qtyPerSmallestUnit", | |||
label: t("Available Qty Per Smallest Unit"), | |||
align: "right", | |||
headerAlign: "right", | |||
type: "integer", | |||
}, | |||
{ | |||
name: "baseUom", | |||
label: t("Base UoM"), | |||
label: t("Stock UoM"), | |||
align: "left", | |||
headerAlign: "left", | |||
}, | |||
// { | |||
// name: "qtyPerSmallestUnit", | |||
// label: t("Available Qty Per Smallest Unit"), | |||
// align: "right", | |||
// headerAlign: "right", | |||
// type: "integer", | |||
// }, | |||
// { | |||
// name: "baseUom", | |||
// label: t("Base UoM"), | |||
// align: "left", | |||
// headerAlign: "left", | |||
// }, | |||
{ | |||
name: "expiryDate", | |||
label: t("Expiry Date"), | |||
@@ -41,25 +41,25 @@ const InventoryTable: React.FC<Props> = ({ inventories, pagingController, setPag | |||
}, | |||
{ | |||
name: "uomUdfudesc", | |||
label: t("Sales UoM"), | |||
align: "left", | |||
headerAlign: "left", | |||
}, | |||
{ | |||
name: "qtyPerSmallestUnit", | |||
label: t("Available Qty Per Smallest Unit"), | |||
align: "right", | |||
headerAlign: "right", | |||
type: "integer", | |||
}, | |||
{ | |||
name: "baseUom", | |||
label: t("Base UoM"), | |||
label: t("Stock UoM"), | |||
align: "left", | |||
headerAlign: "left", | |||
}, | |||
// { | |||
// name: "qtyPerSmallestUnit", | |||
// label: t("Available Qty Per Smallest Unit"), | |||
// align: "right", | |||
// headerAlign: "right", | |||
// type: "integer", | |||
// }, | |||
// { | |||
// name: "baseUom", | |||
// label: t("Base UoM"), | |||
// align: "left", | |||
// headerAlign: "left", | |||
// }, | |||
// { | |||
// name: "qtyPerSmallestUnit", | |||
// label: t("Qty Per Smallest Unit"), | |||
// align: "right", | |||
// headerAlign: "right", | |||
@@ -66,11 +66,9 @@ import DoneIcon from "@mui/icons-material/Done"; | |||
import { getCustomWidth } from "@/app/utils/commonUtil"; | |||
import PoInfoCard from "./PoInfoCard"; | |||
import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; | |||
import { fetchPoListClient } from "@/app/api/po/actions"; | |||
import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material"; | |||
import { createStockInLine } from "@/app/api/dashboard/actions"; | |||
//import { useRouter } from "next/navigation"; | |||
@@ -181,20 +179,13 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
purchaseOrder.pol || [], | |||
); | |||
const pathname = usePathname() | |||
const router = useRouter(); | |||
const searchParams = useSearchParams(); | |||
const [row, setRow] = useState(rows[0]); | |||
const [stockInLine, setStockInLine] = useState(rows[0].stockInLine); | |||
const [stockInLine, setStockInLine] = useState<StockInLine[]>(rows[0].stockInLine); | |||
const [processedQty, setProcessedQty] = useState(rows[0].processed); | |||
// const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status); | |||
//const router = useRouter(); | |||
const router = useRouter(); | |||
const [poList, setPoList] = useState<PoResult[]>([]); | |||
const [selectedPoId, setSelectedPoId] = useState(po.id); | |||
const currentPoId = searchParams.get('id'); | |||
@@ -297,14 +288,18 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
setRows(purchaseOrder.pol || []); | |||
}, [purchaseOrder]); | |||
// useEffect(() => { | |||
// setStockInLine([]) | |||
// }, []); | |||
function Row(props: { row: PurchaseOrderLine }) { | |||
const { row } = props; | |||
const [firstReceiveQty, setFirstReceiveQty] = useState<number>() | |||
// const [firstReceiveQty, setFirstReceiveQty] = useState<number>() | |||
const [secondReceiveQty, setSecondReceiveQty] = useState<number>() | |||
const [open, setOpen] = useState(false); | |||
// const [open, setOpen] = useState(false); | |||
const [processedQty, setProcessedQty] = useState(row.processed); | |||
const [currStatus, setCurrStatus] = useState(row.status); | |||
const [stockInLine, setStockInLine] = useState(row.stockInLine); | |||
// const [stockInLine, setStockInLine] = useState(row.stockInLine); | |||
const totalWeight = useMemo( | |||
() => calculateWeight(row.qty, row.uom), | |||
[row.qty, row.uom], | |||
@@ -314,6 +309,13 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
[row.uom], | |||
); | |||
useEffect(() => { | |||
const polId = searchParams.get("polId") != null ? parseInt(searchParams.get("polId")!) : null | |||
if (polId) { | |||
setStockInLine(rows.find((r) => r.id == polId)!.stockInLine) | |||
} | |||
}, []); | |||
useEffect(() => { | |||
if (processedQty === row.qty) { | |||
setCurrStatus("completed".toUpperCase()); | |||
@@ -330,9 +332,72 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
setStockInLine(row.stockInLine); | |||
setProcessedQty(row.processed); | |||
}; | |||
const changeStockInLines = useCallback( | |||
(id: number) => { | |||
console.log(id) | |||
//rows = purchaseOrderLine | |||
console.log(rows) | |||
const target = rows.find((r) => r.id === id) | |||
const stockInLine = target!.stockInLine | |||
console.log(stockInLine) | |||
setStockInLine(stockInLine) | |||
setRow(target!) | |||
// console.log(pathname) | |||
// router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | |||
}, | |||
[rows] | |||
); | |||
const handleStart = useCallback( | |||
() => { | |||
setTimeout(async () => { | |||
// post stock in line | |||
const oldId = row.id; | |||
const postData = { | |||
itemId: row.itemId, | |||
itemNo: row.itemNo, | |||
itemName: row.itemName, | |||
purchaseOrderId: row.purchaseOrderId, | |||
purchaseOrderLineId: row.id, | |||
acceptedQty: secondReceiveQty || 0, | |||
// acceptedQty: row.acceptedQty, | |||
}; | |||
if (secondReceiveQty === 0) return | |||
const res = await createStockInLine(postData); | |||
console.log(res); | |||
}, 200); | |||
}, | |||
[], | |||
); | |||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => { | |||
const raw = e.target.value; | |||
// Allow empty input | |||
if (raw.trim() === '') { | |||
setSecondReceiveQty(undefined); | |||
return; | |||
} | |||
// Keep digits only | |||
const cleaned = raw.replace(/[^\d]/g, ''); | |||
if (cleaned === '') { | |||
// If the user typed only non-digits, keep previous value | |||
return; | |||
} | |||
// Parse and clamp to non-negative integer | |||
const next = Math.max(0, Math.floor(Number(cleaned))); | |||
setSecondReceiveQty(next); | |||
}; | |||
return ( | |||
<> | |||
<TableRow sx={{ "& > *": { borderBottom: "unset" }, color: "black" }}> | |||
<TableRow | |||
sx={{ "& > *": { borderBottom: "unset" }, | |||
color: "black" | |||
}} | |||
onClick={() => changeStockInLines(row.id)} | |||
> | |||
{/* <TableCell> | |||
<IconButton | |||
disabled={purchaseOrder.status.toLowerCase() === "pending"} | |||
@@ -355,9 +420,9 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
<TableCell align="right">{integerFormatter.format(row.qty)}</TableCell> | |||
<TableCell align="right">{integerFormatter.format(processedQty)}</TableCell> | |||
<TableCell align="left">{row.uom?.code}</TableCell> | |||
<TableCell align="right"> | |||
{/* <TableCell align="right"> | |||
{decimalFormatter.format(totalWeight)} {weightUnit} | |||
</TableCell> | |||
</TableCell> */} | |||
{/* <TableCell align="left">{weightUnit}</TableCell> */} | |||
<TableCell align="right">{decimalFormatter.format(row.price)}</TableCell> | |||
{/* <TableCell align="left">{row.expiryDate}</TableCell> */} | |||
@@ -371,7 +436,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
type="text" // Use type="text" to allow validation in the change handler | |||
variant="outlined" | |||
value={secondReceiveQty} | |||
// onChange={handleChange} | |||
onChange={handleChange} | |||
InputProps={{ | |||
inputProps: { | |||
min: 0, // Optional: set a minimum value | |||
@@ -381,7 +446,12 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
/> | |||
</TableCell> | |||
<TableCell align="center"> | |||
<Button variant="contained"> | |||
<Button | |||
variant="contained" | |||
onClick={() => | |||
handleStart() | |||
} | |||
> | |||
提交 | |||
</Button> | |||
</TableCell> | |||
@@ -503,7 +573,6 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
const renderFieldCondition = useCallback((field: "firstInQty" | "secondInQty"): boolean => { | |||
switch (field) { | |||
case FIRST_IN_FIELD: | |||
return true; | |||
case SECOND_IN_FIELD: | |||
return true; | |||
@@ -512,6 +581,14 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
} | |||
}, []); | |||
useEffect(() => { | |||
const params = searchParams.get("polId") | |||
if (params) { | |||
const polId = parseInt(params) | |||
} | |||
}, [searchParams]) | |||
return ( | |||
<> | |||
<Stack spacing={2}> | |||
@@ -546,12 +623,12 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
type="text" | |||
variant="outlined" | |||
fullWidth | |||
InputProps={{ | |||
inputProps: { | |||
min: 0, | |||
step: 1 | |||
} | |||
}} | |||
// InputProps={{ | |||
// inputProps: { | |||
// min: 0, | |||
// step: 1 | |||
// } | |||
// }} | |||
/> | |||
<TextField | |||
label={t("dnDate")} | |||
@@ -589,7 +666,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
<TableCell align="right">{t("qty")}</TableCell> | |||
<TableCell align="right">{t("processed")}</TableCell> | |||
<TableCell align="left">{t("uom")}</TableCell> | |||
<TableCell align="right">{t("total weight")}</TableCell> | |||
{/* <TableCell align="right">{t("total weight")}</TableCell> */} | |||
<TableCell align="right">{`${t("price")} (HKD)`}</TableCell> | |||
<TableCell align="left" sx={{ width: '75px' }}>{t("status")}</TableCell> | |||
{renderFieldCondition(FIRST_IN_FIELD) ? <TableCell align="right">{t("receivedQty")}</TableCell> : undefined} | |||
@@ -121,6 +121,9 @@ function PoInputGrid({ | |||
); | |||
console.log(stockInLine); | |||
const [entries, setEntries] = useState<StockInLineRow[]>(stockInLine || []); | |||
useEffect(() => { | |||
setEntries(stockInLine) | |||
}, [stockInLine]) | |||
const [modalInfo, setModalInfo] = useState< | |||
StockInLine & { qcResult?: PurchaseQcResult[] } | |||
>(); | |||
@@ -278,16 +281,11 @@ const closeNewModal = useCallback(() => { | |||
setNewOpen(true); | |||
}, []); | |||
// Open modal if `stockInLineId` exists in the URL | |||
useEffect(() => { | |||
if (stockInLineId && !newOpen) { | |||
openNewModal(); | |||
} | |||
}, [stockInLineId, newOpen, openNewModal]); | |||
// Button handler to update the URL and open the modal | |||
const handleNewQC = useCallback( | |||
(id: GridRowId, params: any) => async () => { | |||
console.log(id) | |||
console.log(params) | |||
setBtnIsLoading(true); | |||
setRowModesModel((prev) => ({ | |||
...prev, | |||
@@ -304,12 +302,21 @@ const closeNewModal = useCallback(() => { | |||
const newParams = new URLSearchParams(searchParams.toString()); | |||
newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates | |||
router.replace(`${pathname}?${newParams.toString()}`); | |||
console.log("hello") | |||
openNewModal() | |||
setBtnIsLoading(false); | |||
}, 200); | |||
}, | |||
[fetchQcDefaultValue, searchParams, router, pathname] | |||
[fetchQcDefaultValue, openNewModal, pathname, router, searchParams] | |||
); | |||
// Open modal if `stockInLineId` exists in the URL | |||
useEffect(() => { | |||
if (stockInLineId) { | |||
console.log("heeloo") | |||
console.log(stockInLineId) | |||
handleNewQC(stockInLineId, apiRef.current.getRow(stockInLineId)); | |||
} | |||
}, [stockInLineId, newOpen, handleNewQC, apiRef]); | |||
const handleEscalation = useCallback( | |||
(id: GridRowId, params: any) => () => { | |||
// setBtnIsLoading(true); | |||
@@ -476,20 +483,20 @@ const closeNewModal = useCallback(() => { | |||
return params.row.uom.code; | |||
}, | |||
}, | |||
{ | |||
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: "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"), | |||
@@ -281,7 +281,7 @@ function SearchResults<T extends ResultWithId>({ | |||
setCheckboxIds(newSelected); | |||
} | |||
}, | |||
[checkboxIds], | |||
[checkboxIds, setCheckboxIds], | |||
); | |||
const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => { | |||
@@ -29,5 +29,7 @@ | |||
"Pending application": "待處理提料申請", | |||
"pending inspection material": "待品檢物料", | |||
"inspected material": "已品檢物料", | |||
"total material": "物料總數" | |||
"total material": "物料總數", | |||
"stock in escalation list": "收貨已上報列表" | |||
} |
@@ -8,12 +8,13 @@ | |||
"UoM": "單位", | |||
"mat": "物料", | |||
"fg": "成品", | |||
"Available Qty": "可用數量 (銷售單位)", | |||
"Available Qty": "可用數量 (倉存單位)", | |||
"Sales UoM": "銷售單位", | |||
"Stock UoM": "倉存單位", | |||
"Available Qty Per Smallest Unit": "可用數量 (基本單位)", | |||
"Base UoM": "基本單位", | |||
"Lot No": "批號", | |||
"Expiry Date": "到期日", | |||
"No items are selected yet.": "未選擇項目", | |||
"Item selected": "已選擇項目" | |||
} | |||
} |
@@ -10,6 +10,7 @@ | |||
"Product / Material": "產品 / 材料", | |||
"Product / Material Details": "產品 / 材料詳情", | |||
"Qc items": "QC 項目", | |||
"Qc Category": "質檢模板", | |||
"Name": "名稱", | |||
"name": "名稱", | |||
"description": "描述", | |||