# Conflicts: # src/app/api/pickOrder/actions.tsmaster
@@ -4,6 +4,7 @@ import { Suspense } from "react"; | |||||
import { getServerI18n } from "@/i18n"; | import { getServerI18n } from "@/i18n"; | ||||
import DashboardPage from "@/components/DashboardPage"; | import DashboardPage from "@/components/DashboardPage"; | ||||
import { SearchParams } from "@/app/utils/fetchUtil"; | import { SearchParams } from "@/app/utils/fetchUtil"; | ||||
import { fetchEscalationLogsByUser } from "@/app/api/escalation"; | |||||
export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
title: "Dashboard", | title: "Dashboard", | ||||
@@ -14,6 +15,8 @@ type Props = {} & SearchParams; | |||||
const Dashboard: React.FC<Props> = async ({ searchParams }) => { | const Dashboard: React.FC<Props> = async ({ searchParams }) => { | ||||
const { t } = await getServerI18n("dashboard"); | const { t } = await getServerI18n("dashboard"); | ||||
fetchEscalationLogsByUser() | |||||
return ( | return ( | ||||
<I18nProvider namespaces={["dashboard", "common"]}> | <I18nProvider namespaces={["dashboard", "common"]}> | ||||
<Suspense fallback={<DashboardPage.Loading />}> | <Suspense fallback={<DashboardPage.Loading />}> | ||||
@@ -37,7 +37,7 @@ export default async function MainLayout({ | |||||
return ( | return ( | ||||
<SessionProviderWrapper session={session}> | <SessionProviderWrapper session={session}> | ||||
<UploadProvider> | <UploadProvider> | ||||
<CameraProvider> | |||||
{/* <CameraProvider> */} | |||||
<AxiosProvider> | <AxiosProvider> | ||||
<QrCodeScannerProvider> | <QrCodeScannerProvider> | ||||
<> | <> | ||||
@@ -62,7 +62,7 @@ export default async function MainLayout({ | |||||
</> | </> | ||||
</QrCodeScannerProvider> | </QrCodeScannerProvider> | ||||
</AxiosProvider> | </AxiosProvider> | ||||
</CameraProvider> | |||||
{/* </CameraProvider> */} | |||||
</UploadProvider> | </UploadProvider> | ||||
</SessionProviderWrapper> | </SessionProviderWrapper> | ||||
); | ); | ||||
@@ -1,3 +1,4 @@ | |||||
import { fetchEscalationCombo } from "@/app/api/user"; | |||||
import { SearchParams } from "@/app/utils/fetchUtil"; | import { SearchParams } from "@/app/utils/fetchUtil"; | ||||
import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
import CreateProductMaterial from "@/components/CreateItem"; | import CreateProductMaterial from "@/components/CreateItem"; | ||||
@@ -22,6 +23,9 @@ const PoEdit: React.FC<Props> = async ({ searchParams }) => { | |||||
if (!id) { | if (!id) { | ||||
notFound(); | notFound(); | ||||
} | } | ||||
fetchEscalationCombo() | |||||
return ( | return ( | ||||
<> | <> | ||||
{/* <Typography variant="h4">{t("Create Material")}</Typography> */} | {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | ||||
@@ -30,9 +30,6 @@ export interface StockInLineEntry { | |||||
acceptedQty: number; | acceptedQty: number; | ||||
status?: string; | status?: string; | ||||
expiryDate?: string; | expiryDate?: string; | ||||
productLotNo?: string; | |||||
receiptDate?: string; | |||||
dnDate?: string; | |||||
} | } | ||||
export interface PurchaseQcResult { | export interface PurchaseQcResult { | ||||
@@ -6,7 +6,7 @@ import { serverFetchJson } from "../../utils/fetchUtil"; | |||||
import { BASE_API_URL } from "../../../config/api"; | import { BASE_API_URL } from "../../../config/api"; | ||||
import { Uom } from "../settings/uom"; | import { Uom } from "../settings/uom"; | ||||
import { RecordsRes } from "../utils"; | import { RecordsRes } from "../utils"; | ||||
import { IQCItems } from "@/components/DashboardPage/QC/SupervisorQcApproval"; | |||||
// import { IQCItems } from "@/components/DashboardPage/QC/SupervisorQcApproval"; | |||||
export interface PoResult { | export interface PoResult { | ||||
id: number; | id: number; | ||||
@@ -84,7 +84,8 @@ export const fetchPoWithStockInLines = cache(async (id: number) => { | |||||
}); | }); | ||||
export const fetchIqcLogByUser = cache(async () => { | export const fetchIqcLogByUser = cache(async () => { | ||||
return serverFetchJson<IQCItems[]>(`${BASE_API_URL}/supervisionApprovalLog/stock-in`, { | |||||
next: { tags: ["qcLog"] }, | |||||
}); | |||||
// return serverFetchJson<IQCItems[]>(`${BASE_API_URL}/supervisionApprovalLog/stock-in`, { | |||||
// next: { tags: ["qcLog"] }, | |||||
// }); | |||||
return undefined; | |||||
}); | }); |
@@ -0,0 +1,58 @@ | |||||
"server only" | |||||
import { convertObjToURLSearchParams } from "@/app/utils/commonUtil"; | |||||
import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||||
import { BASE_API_URL } from "@/config/api"; | |||||
import { cache } from "react"; | |||||
export interface EscalationResult { | |||||
id: number; | |||||
handler?: string; | |||||
handlerDepartment?: string; | |||||
handlerName?: string; | |||||
handlerTitle?: string; | |||||
polId?: number; | |||||
poId?: number; | |||||
reason?: string; | |||||
handlerId?: number; | |||||
itemName?: string; | |||||
demandQty?: number; | |||||
acceptedQty?: number; | |||||
purchaseUomCode?: string; | |||||
purchaseUomDesc?: string; | |||||
stockUomCode?: string; | |||||
stockUomDesc?: string; | |||||
stockInLineId?: number; | |||||
stockOutLineId?: number; | |||||
qcFailCount?: number; | |||||
qcTotalCount?: number; | |||||
poCode?: string; | |||||
itemCode?: string; | |||||
dnDate?: number[]; | |||||
dnNo?: string; | |||||
} | |||||
export const fetchEscalationLogsByStockInLines = cache(async(stockInLineIds: number[]) => { | |||||
const searchParams = convertObjToURLSearchParams({stockInLineIds: stockInLineIds}) | |||||
return serverFetchJson<EscalationResult[]>(`${BASE_API_URL}/escalationLog/stockInLines?${searchParams}`, | |||||
{ | |||||
method: "GET", | |||||
headers: { "Content-Type": "application/json" }, | |||||
next: { | |||||
tags: ["escalationLogs"], | |||||
}, | |||||
}, | |||||
); | |||||
}); | |||||
export const fetchEscalationLogsByUser = cache(async() => { | |||||
return serverFetchJson<EscalationResult[]>(`${BASE_API_URL}/escalationLog/user`, | |||||
{ | |||||
method: "GET", | |||||
headers: { "Content-Type": "application/json" }, | |||||
next: { | |||||
tags: ["escalationLogs"], | |||||
}, | |||||
}, | |||||
); | |||||
}); |
@@ -0,0 +1,17 @@ | |||||
"use server"; | |||||
import { serverFetchBlob } from "@/app/utils/fetchUtil"; | |||||
import { BASE_API_URL } from "@/config/api"; | |||||
import { cache } from "react"; | |||||
export const getMailTemplateForStockInLine = cache(async (stockInLineId: number) => { | |||||
console.log("stockInLineId", stockInLineId) | |||||
return serverFetchBlob(`${BASE_API_URL}/mailTemplates/getMailTemplateForStockInLine/${stockInLineId}`, | |||||
{ | |||||
method: "GET", | |||||
headers: { "Content-Type": "application/json" }, | |||||
next: { | |||||
tags: ["mailTemplateForStockInLine"], | |||||
}, | |||||
}) | |||||
}) |
@@ -144,47 +144,6 @@ export interface PickOrderLotDetailResponse { | |||||
} | } | ||||
export interface AssignPickOrderInputs { | |||||
pickOrderIds: number[]; | |||||
assignTo: number; | |||||
} | |||||
export const newassignPickOrder = async (data: AssignPickOrderInputs) => { | |||||
const pickOrder = await serverFetchJson<any>( | |||||
`${BASE_API_URL}/pickOrder/assign`, | |||||
{ | |||||
method: "POST", | |||||
body: JSON.stringify(data), | |||||
headers: { "Content-Type": "application/json" }, | |||||
}, | |||||
); | |||||
revalidateTag("pickorder"); | |||||
return pickOrder; | |||||
}; | |||||
export const newreleasePickOrder = async (data: AssignPickOrderInputs) => { | |||||
const pickOrder = await serverFetchJson<any>( | |||||
`${BASE_API_URL}/pickOrder/release`, | |||||
{ | |||||
method: "POST", | |||||
body: JSON.stringify(data), | |||||
headers: { "Content-Type": "application/json" }, | |||||
}, | |||||
); | |||||
revalidateTag("pickorder"); | |||||
return pickOrder; | |||||
}; | |||||
export const releaseAssignedPickOrders = async (data: AssignPickOrderInputs) => { | |||||
const pickOrder = await serverFetchJson<any>( | |||||
`${BASE_API_URL}/pickOrder/release-assigned`, | |||||
{ | |||||
method: "POST", | |||||
body: JSON.stringify(data), | |||||
headers: { "Content-Type": "application/json" }, | |||||
}, | |||||
); | |||||
revalidateTag("pickorder"); | |||||
return pickOrder; | |||||
}; | |||||
export const fetchAllPickOrderDetails = cache(async () => { | export const fetchAllPickOrderDetails = cache(async () => { | ||||
return serverFetchJson<GetPickOrderInfoResponse>( | return serverFetchJson<GetPickOrderInfoResponse>( | ||||
`${BASE_API_URL}/pickOrder/detail`, | `${BASE_API_URL}/pickOrder/detail`, | ||||
@@ -31,13 +31,17 @@ export interface StockInLineEntry { | |||||
acceptedQty: number; | acceptedQty: number; | ||||
status?: string; | status?: string; | ||||
expiryDate?: string; | expiryDate?: string; | ||||
productLotNo?: string; | |||||
receiptDate?: string; | |||||
dnDate?: string; | |||||
} | } | ||||
export interface PurchaseQcResult { | |||||
export interface PurchaseQcResult{ | |||||
qcItemId: number; | qcItemId: number; | ||||
isPassed: boolean, | |||||
qcPassed: boolean; | |||||
failQty: number; | failQty: number; | ||||
remarks?: string | |||||
remarks?: string; | |||||
} | } | ||||
export interface StockInInput { | export interface StockInInput { | ||||
status: string; | status: string; | ||||
@@ -110,7 +114,7 @@ export const fetchStockInLineInfo = cache(async (stockInLineId: number) => { | |||||
export const createStockInLine = async (data: StockInLineEntry) => { | export const createStockInLine = async (data: StockInLineEntry) => { | ||||
const stockInLine = await serverFetchJson< | const stockInLine = await serverFetchJson< | ||||
PostStockInLineResponse<StockInLineEntry> | |||||
PostStockInLineResponse<StockInLine> | |||||
>(`${BASE_API_URL}/stockInLine/create`, { | >(`${BASE_API_URL}/stockInLine/create`, { | ||||
method: "POST", | method: "POST", | ||||
body: JSON.stringify(data), | body: JSON.stringify(data), | ||||
@@ -124,7 +128,7 @@ export const updateStockInLine = async ( | |||||
data: StockInLineEntry & ModalFormInput, | data: StockInLineEntry & ModalFormInput, | ||||
) => { | ) => { | ||||
const stockInLine = await serverFetchJson< | const stockInLine = await serverFetchJson< | ||||
PostStockInLineResponse<StockInLineEntry & ModalFormInput> | |||||
PostStockInLineResponse<StockInLine & ModalFormInput> | |||||
>(`${BASE_API_URL}/stockInLine/update`, { | >(`${BASE_API_URL}/stockInLine/update`, { | ||||
method: "POST", | method: "POST", | ||||
body: JSON.stringify(data), | body: JSON.stringify(data), | ||||
@@ -15,6 +15,11 @@ export interface PoResult { | |||||
estimatedArrivalDate: string; | estimatedArrivalDate: string; | ||||
completedDate: string; | completedDate: string; | ||||
itemDetail?: string; | itemDetail?: string; | ||||
itemCode?: string; | |||||
itemName?: string; | |||||
itemQty?: string; | |||||
itemSumAcceptedQty?: string; | |||||
itemUom?: string; | |||||
escalated: boolean; | escalated: boolean; | ||||
status: string; | status: string; | ||||
pol?: PurchaseOrderLine[]; | pol?: PurchaseOrderLine[]; | ||||
@@ -49,7 +54,7 @@ export interface StockUomForPoLine { | |||||
export interface StockInLine { | export interface StockInLine { | ||||
id: number; | id: number; | ||||
stockInId: number; | |||||
stockInId?: number; | |||||
purchaseOrderId?: number; | purchaseOrderId?: number; | ||||
purchaseOrderLineId: number; | purchaseOrderLineId: number; | ||||
itemId: number; | itemId: number; | ||||
@@ -58,22 +63,24 @@ export interface StockInLine { | |||||
itemType: string; | itemType: string; | ||||
demandQty: number; | demandQty: number; | ||||
acceptedQty: number; | acceptedQty: number; | ||||
qty: number; | |||||
processed: number; | |||||
price: number; | |||||
priceUnit: string; | |||||
qty?: number; | |||||
processed?: number; | |||||
price?: number; | |||||
priceUnit?: string; | |||||
shelfLife?: number; | shelfLife?: number; | ||||
receiptDate?: string; | receiptDate?: string; | ||||
productionDate?: string; | productionDate?: string; | ||||
productLotNo?: string; | |||||
expiryDate?: string; | expiryDate?: string; | ||||
status: string; | status: string; | ||||
supplier: string; | |||||
lotNo: string; | |||||
poCode: string; | |||||
uom: Uom; | |||||
supplier?: string; | |||||
lotNo?: string; | |||||
poCode?: string; | |||||
uom?: Uom; | |||||
defaultWarehouseId: number; // id for now | defaultWarehouseId: number; // id for now | ||||
dnNo: string; | |||||
dnDate: number[]; | |||||
dnNo?: string; | |||||
dnDate?: number[]; | |||||
stockQty?: number; | |||||
} | } | ||||
export const fetchPoList = cache(async (queryParams?: Record<string, any>) => { | export const fetchPoList = cache(async (queryParams?: Record<string, any>) => { | ||||
@@ -15,6 +15,16 @@ export interface QcItemWithChecks { | |||||
description: string | undefined; | description: string | undefined; | ||||
} | } | ||||
export interface QcData { | |||||
id: number, | |||||
code: string, | |||||
name: string, | |||||
qcDescription: string, | |||||
qcPassed: boolean | undefined | |||||
failQty: number | undefined | |||||
remarks: string | undefined | |||||
} | |||||
export const fetchQcItemCheckList = cache(async () => { | export const fetchQcItemCheckList = cache(async () => { | ||||
return serverFetchJson<QcItemWithChecks[]>(`${BASE_API_URL}/qc/list`, { | return serverFetchJson<QcItemWithChecks[]>(`${BASE_API_URL}/qc/list`, { | ||||
next: { tags: ["qc"] }, | next: { tags: ["qc"] }, | ||||
@@ -31,6 +31,15 @@ export type passwordRule = { | |||||
specialChar: boolean; | specialChar: boolean; | ||||
}; | }; | ||||
export interface EscalationCombo { | |||||
id: number; | |||||
value: number; | |||||
label: string; | |||||
name: string; | |||||
title: string; | |||||
department: string; | |||||
} | |||||
export const preloadUser = () => { | export const preloadUser = () => { | ||||
fetchUser(); | fetchUser(); | ||||
}; | }; | ||||
@@ -56,3 +65,9 @@ export const fetchPwRules = cache(async () => { | |||||
next: { tags: ["pwRule"] }, | next: { tags: ["pwRule"] }, | ||||
}); | }); | ||||
}); | }); | ||||
export const fetchEscalationCombo = cache(async () => { | |||||
return serverFetchJson<EscalationCombo>(`${BASE_API_URL}/user/escalation-combo`, { | |||||
next: { tags: ["escalationCombo"]} | |||||
}) | |||||
}) |
@@ -3,6 +3,11 @@ import { getServerSession } from "next-auth"; | |||||
import { headers } from "next/headers"; | import { headers } from "next/headers"; | ||||
import { redirect } from "next/navigation"; | import { redirect } from "next/navigation"; | ||||
export interface BlobResponse { | |||||
filename: string; | |||||
blobValue: Uint8Array; | |||||
} | |||||
export interface Pageable { | export interface Pageable { | ||||
pageSize?: number; | pageSize?: number; | ||||
pageNum?: number; | pageNum?: number; | ||||
@@ -86,7 +91,7 @@ export async function serverFetchJson<T>(...args: FetchParams) { | |||||
} | } | ||||
} | } | ||||
export async function serverFetchBlob<T>(...args: FetchParams) { | |||||
export async function serverFetchBlob<T extends BlobResponse>(...args: FetchParams) { | |||||
const response = await serverFetch(...args); | const response = await serverFetch(...args); | ||||
if (response.ok) { | if (response.ok) { | ||||
@@ -14,13 +14,17 @@ import ApplicationCompletionChart from "./chart/ApplicationCompletionChart"; | |||||
import OrderCompletionChart from "./chart/OrderCompletionChart"; | import OrderCompletionChart from "./chart/OrderCompletionChart"; | ||||
import DashboardBox from "./Dashboardbox"; | import DashboardBox from "./Dashboardbox"; | ||||
import CollapsibleCard from "./CollapsibleCard"; | import CollapsibleCard from "./CollapsibleCard"; | ||||
import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval"; | |||||
// import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval"; | |||||
import { EscalationResult } from "@/app/api/escalation"; | |||||
import EscalationLogTable from "./escalation/EscalationLogTable"; | |||||
type Props = { | type Props = { | ||||
iqc: IQCItems[] | |||||
// iqc: IQCItems[] | undefined | |||||
escalationLogs: EscalationResult[] | |||||
}; | }; | ||||
const DashboardPage: React.FC<Props> = ({ | const DashboardPage: React.FC<Props> = ({ | ||||
iqc | |||||
// iqc, | |||||
escalationLogs | |||||
}) => { | }) => { | ||||
const { t } = useTranslation("dashboard"); | const { t } = useTranslation("dashboard"); | ||||
const router = useRouter(); | const router = useRouter(); | ||||
@@ -29,9 +33,9 @@ const DashboardPage: React.FC<Props> = ({ | |||||
<ThemeProvider theme={theme}> | <ThemeProvider theme={theme}> | ||||
<Grid container spacing={2}> | <Grid container spacing={2}> | ||||
<Grid item xs={12}> | <Grid item xs={12}> | ||||
<CollapsibleCard title={t("stock in escalation list")}> | |||||
<CollapsibleCard title={t("Escalation List")}> | |||||
<CardContent> | <CardContent> | ||||
<SupervisorQcApproval items={iqc || []}/> | |||||
<EscalationLogTable items={escalationLogs || []}/> | |||||
</CardContent> | </CardContent> | ||||
</CollapsibleCard> | </CollapsibleCard> | ||||
</Grid> | </Grid> | ||||
@@ -4,7 +4,8 @@ import DashboardPage from "./DashboardPage"; | |||||
import { Typography } from "@mui/material"; | import { Typography } from "@mui/material"; | ||||
import { I18nProvider, getServerI18n } from "@/i18n"; | import { I18nProvider, getServerI18n } from "@/i18n"; | ||||
import DashboardLoading from "./DashboardLoading"; | import DashboardLoading from "./DashboardLoading"; | ||||
import { fetchIqcLogByUser } from "@/app/api/dashboard"; | |||||
import { fetchEscalationLogsByUser } from "@/app/api/escalation"; | |||||
// import { fetchIqcLogByUser } from "@/app/api/dashboard"; | |||||
// export type SessionWithAbilities = { | // export type SessionWithAbilities = { | ||||
// abilities: string[] | // abilities: string[] | ||||
@@ -23,16 +24,20 @@ const DashboardWrapper: React.FC<Props> & SubComponents = async ({ | |||||
}) => { | }) => { | ||||
const { t } = await getServerI18n("dashboard"); | const { t } = await getServerI18n("dashboard"); | ||||
// const session: SessionWithAbilities = await getServerSession(authOptions) | // const session: SessionWithAbilities = await getServerSession(authOptions) | ||||
const [iqcLog] = await Promise.all([ | |||||
fetchIqcLogByUser() | |||||
// const [iqcLog] = await Promise.all([ | |||||
// fetchIqcLogByUser() | |||||
// ]) | |||||
const [escalationLogs] = await Promise.all([ | |||||
fetchEscalationLogsByUser() | |||||
]) | ]) | ||||
console.log(iqcLog) | |||||
return ( | return ( | ||||
<> | <> | ||||
<Typography variant="h4">{t("Dashboard")}</Typography> | <Typography variant="h4">{t("Dashboard")}</Typography> | ||||
<DashboardPage | <DashboardPage | ||||
iqc={iqcLog} | |||||
escalationLogs={escalationLogs} | |||||
// iqc={iqcLog} | |||||
// abilities={session ? session?.abilities : []} | // abilities={session ? session?.abilities : []} | ||||
/> | /> | ||||
</> | </> | ||||
@@ -2,9 +2,13 @@ | |||||
import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material"; | import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material"; | ||||
import { useRouter } from "next/navigation"; | import { useRouter } from "next/navigation"; | ||||
import { useCallback, useState } from "react"; | |||||
import { useCallback, useMemo, useState } from "react"; | |||||
import { usePathname } from "next/navigation"; | import { usePathname } from "next/navigation"; | ||||
import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
import { EscalationResult } from "@/app/api/escalation"; | |||||
import { Column } from "@/components/SearchResults"; | |||||
import SearchResults from "@/components/SearchResults/SearchResults"; | |||||
import { arrayToDateString } from "@/app/utils/formatUtil"; | |||||
export type IQCItems = { | export type IQCItems = { | ||||
id: number; | id: number; | ||||
@@ -18,11 +22,11 @@ export type IQCItems = { | |||||
}; | }; | ||||
type Props = { | type Props = { | ||||
items: IQCItems[]; | |||||
items: EscalationResult[]; | |||||
}; | }; | ||||
const SupervisorQcApproval: React.FC<Props> = ({ | |||||
items | |||||
const EscalationLogTable: React.FC<Props> = ({ | |||||
items | |||||
}) => { | }) => { | ||||
const { t } = useTranslation("dashboard"); | const { t } = useTranslation("dashboard"); | ||||
const CARD_HEADER = t("stock in escalation list") | const CARD_HEADER = t("stock in escalation list") | ||||
@@ -31,8 +35,8 @@ const SupervisorQcApproval: React.FC<Props> = ({ | |||||
const router = useRouter(); | const router = useRouter(); | ||||
const [selectedId, setSelectedId] = useState<number | null>(null); | const [selectedId, setSelectedId] = useState<number | null>(null); | ||||
const navigateTo = useCallback( | |||||
(item: IQCItems) => { | |||||
const navigateTo = useCallback( | |||||
(item: EscalationResult) => { | |||||
setSelectedId(item.id); | setSelectedId(item.id); | ||||
console.log(pathname) | console.log(pathname) | ||||
router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | ||||
@@ -40,17 +44,62 @@ const navigateTo = useCallback( | |||||
[router, pathname] | [router, pathname] | ||||
); | ); | ||||
const handleKeyDown = useCallback( | |||||
(e: React.KeyboardEvent, item: IQCItems) => { | |||||
if (e.key === 'Enter' || e.key === ' ') { | |||||
e.preventDefault(); | |||||
navigateTo(item); | |||||
} | |||||
}, | |||||
[navigateTo] | |||||
); | |||||
const onRowClick = useCallback((item: EscalationResult) => { | |||||
router.push(`/po/edit?id=${item.poId}&selectedIds=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | |||||
}, [router]) | |||||
// const handleKeyDown = useCallback( | |||||
// (e: React.KeyboardEvent, item: EscalationResult) => { | |||||
// if (e.key === 'Enter' || e.key === ' ') { | |||||
// e.preventDefault(); | |||||
// navigateTo(item); | |||||
// } | |||||
// }, | |||||
// [navigateTo] | |||||
// ); | |||||
return ( | |||||
const columns = useMemo<Column<EscalationResult>[]>( | |||||
() => [ | |||||
{ | |||||
name: "handler", | |||||
label: t("Responsible for handling colleagues") | |||||
}, | |||||
{ | |||||
name: "acceptedQty", | |||||
label: t("Received Qty"), | |||||
align: "right", | |||||
headerAlign: "right" | |||||
}, | |||||
{ | |||||
name: "purchaseUomDesc", | |||||
label: t("Purchase UoM") | |||||
}, | |||||
{ | |||||
name: "dnDate", | |||||
label: t("DN Date"), | |||||
renderCell: (params) => { | |||||
return params.dnDate ? arrayToDateString(params.dnDate) : "N/A" | |||||
} | |||||
}, | |||||
{ | |||||
name: "qcTotalCount", | |||||
label: t("QC Completed Count"), | |||||
align: "right", | |||||
headerAlign: "right" | |||||
}, | |||||
{ | |||||
name: "qcFailCount", | |||||
label: t("QC Fail Count"), | |||||
align: "right", | |||||
headerAlign: "right" | |||||
}, | |||||
{ | |||||
name: "reason", | |||||
label: t("Reason"), | |||||
}, | |||||
], []) | |||||
{/* return ( | |||||
<TableContainer component={Paper}> | <TableContainer component={Paper}> | ||||
<Table aria-label="Two column navigable table" size="small"> | <Table aria-label="Two column navigable table" size="small"> | ||||
<TableHead> | <TableHead> | ||||
@@ -87,7 +136,15 @@ const navigateTo = useCallback( | |||||
</TableBody> | </TableBody> | ||||
</Table> | </Table> | ||||
</TableContainer> | </TableContainer> | ||||
); | |||||
}; | |||||
);*/} | |||||
return ( | |||||
<SearchResults | |||||
onRowClick={onRowClick} | |||||
items={items} | |||||
columns={columns} | |||||
isAutoPaging={false} | |||||
/> | |||||
) | |||||
}; | |||||
export default SupervisorQcApproval; | |||||
export default EscalationLogTable; |
@@ -0,0 +1,19 @@ | |||||
import {Box, CircularProgress, Grid} from "@mui/material"; | |||||
export const LoadingComponent: React.FC = () => { | |||||
return ( | |||||
<> | |||||
<Grid item xs={12} md={12} lg={12} justifyContent="space-between" alignItems="center" marginTop={10}> | |||||
<Box | |||||
display="flex" | |||||
justifyContent="center" | |||||
alignItems="center" | |||||
// autoheight="true" | |||||
> | |||||
<CircularProgress /> | |||||
</Box> | |||||
</Grid> | |||||
</> | |||||
) | |||||
} | |||||
export default LoadingComponent; |
@@ -84,6 +84,7 @@ export interface SelectionInputDataGridProps<T, V, E> { | |||||
columns: GridColDef[]; | columns: GridColDef[]; | ||||
validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E; | validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E; | ||||
needAdd?: boolean; | needAdd?: boolean; | ||||
showRemoveBtn?: boolean; | |||||
} | } | ||||
export type Props<T, V, E> = | export type Props<T, V, E> = | ||||
@@ -49,7 +49,7 @@ import EscalationComponent from "./EscalationComponent"; | |||||
import QcDataGrid from "./QCDatagrid"; | import QcDataGrid from "./QCDatagrid"; | ||||
import StockInFormVer2 from "./StockInFormVer2"; | import StockInFormVer2 from "./StockInFormVer2"; | ||||
import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate"; | import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate"; | ||||
import { ModalFormInput } from "@/app/api/dashboard/actions"; | |||||
import { ModalFormInput } from "@/app/api/po/actions"; | |||||
import { escape } from "lodash"; | import { escape } from "lodash"; | ||||
interface Props { | interface Props { | ||||
@@ -83,10 +83,18 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
return ( | return ( | ||||
// <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}> | // <Paper elevation={3} sx={{ maxWidth: 400, mx: 'auto', p: 3 }}> | ||||
<> | <> | ||||
<Paper> | |||||
<Paper sx={{padding: 2}}> | |||||
{/* <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> */} | {/* <Paper elevation={3} sx={{ mx: 'auto', p: 3 }}> */} | ||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> | <Box sx={{ display: 'flex', alignItems: 'center', mb: 2 }}> | ||||
<FormControlLabel | |||||
<Box sx={{ display: 'flex', alignItems: 'center' }}> | |||||
<Typography variant="body1">上報結果</Typography> | |||||
{/* {isCollapsed ? ( | |||||
<ExpandLessIcon sx={{ ml: 1 }} /> | |||||
) : ( | |||||
<ExpandMoreIcon sx={{ ml: 1 }} /> | |||||
)} */} | |||||
</Box> | |||||
{/* <FormControlLabel | |||||
control={ | control={ | ||||
<Checkbox | <Checkbox | ||||
checked={isCollapsed} | checked={isCollapsed} | ||||
@@ -104,23 +112,10 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
)} | )} | ||||
</Box> | </Box> | ||||
} | } | ||||
/> | |||||
/> */} | |||||
</Box> | </Box> | ||||
<Collapse in={isCollapsed}> | <Collapse in={isCollapsed}> | ||||
<Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | <Box component="form" onSubmit={handleSubmit} sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}> | ||||
{forSupervisor ? ( | |||||
<FormControl> | |||||
<RadioGroup | |||||
row | |||||
aria-labelledby="demo-radio-buttons-group-label" | |||||
defaultValue="pass" | |||||
name="radio-buttons-group" | |||||
> | |||||
<FormControlLabel value="pass" control={<Radio />} label="合格" /> | |||||
<FormControlLabel value="fail" control={<Radio />} label="不合格" /> | |||||
</RadioGroup> | |||||
</FormControl> | |||||
): undefined} | |||||
<FormControl fullWidth> | <FormControl fullWidth> | ||||
<select | <select | ||||
id="name" | id="name" | ||||
@@ -136,7 +131,31 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
))} | ))} | ||||
</select> | </select> | ||||
</FormControl> | </FormControl> | ||||
<TextField | |||||
{forSupervisor ? ( | |||||
<FormControl> | |||||
<RadioGroup | |||||
row | |||||
aria-labelledby="demo-radio-buttons-group-label" | |||||
defaultValue="pass" | |||||
name="radio-buttons-group" | |||||
> | |||||
<FormControlLabel value="pass" control={<Radio />} label="合格" /> | |||||
<FormControlLabel value="fail" control={<Radio />} label="不合格" /> | |||||
</RadioGroup> | |||||
</FormControl> | |||||
): undefined} | |||||
{forSupervisor && (<TextField | |||||
fullWidth | |||||
id="decision" | |||||
name="decision" | |||||
label="上報結果" | |||||
type="radio" | |||||
// value={formData.decision} | |||||
onChange={handleInputChange} | |||||
InputProps={{ inputProps: { min: 1 } }} | |||||
placeholder="請決定上報結果" | |||||
/>)} | |||||
{/* <TextField | |||||
fullWidth | fullWidth | ||||
id="quantity" | id="quantity" | ||||
name="quantity" | name="quantity" | ||||
@@ -146,21 +165,21 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
onChange={handleInputChange} | onChange={handleInputChange} | ||||
InputProps={{ inputProps: { min: 1 } }} | InputProps={{ inputProps: { min: 1 } }} | ||||
placeholder="請輸入數量" | placeholder="請輸入數量" | ||||
/> | |||||
/> */} | |||||
<TextField | <TextField | ||||
fullWidth | fullWidth | ||||
id="message" | id="message" | ||||
name="message" | name="message" | ||||
label="備註" | |||||
label="上報原因" | |||||
multiline | multiline | ||||
rows={4} | rows={4} | ||||
value={formData.message} | value={formData.message} | ||||
onChange={handleInputChange} | onChange={handleInputChange} | ||||
placeholder="請輸入您的備註" | |||||
placeholder="請輸入上報原因" | |||||
/> | /> | ||||
<Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
{/* <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
<Button | <Button | ||||
type="submit" | type="submit" | ||||
variant="contained" | variant="contained" | ||||
@@ -168,7 +187,7 @@ const EscalationComponent: React.FC<Props> = ({ | |||||
> | > | ||||
{t("update qc info")} | {t("update qc info")} | ||||
</Button> | </Button> | ||||
</Stack> | |||||
</Stack> */} | |||||
</Box> | </Box> | ||||
</Collapse> | </Collapse> | ||||
</Paper> | </Paper> | ||||
@@ -43,9 +43,11 @@ import { | |||||
import { | import { | ||||
checkPolAndCompletePo, | checkPolAndCompletePo, | ||||
fetchPoInClient, | fetchPoInClient, | ||||
fetchPoListClient, | |||||
fetchStockInLineInfo, | fetchStockInLineInfo, | ||||
PurchaseQcResult, | PurchaseQcResult, | ||||
startPo, | startPo, | ||||
createStockInLine | |||||
} from "@/app/api/po/actions"; | } from "@/app/api/po/actions"; | ||||
import { | import { | ||||
useCallback, | useCallback, | ||||
@@ -66,21 +68,20 @@ import PoQcStockInModal from "./PoQcStockInModal"; | |||||
import QrModal from "./QrModal"; | import QrModal from "./QrModal"; | ||||
import { PlayArrow } from "@mui/icons-material"; | import { PlayArrow } from "@mui/icons-material"; | ||||
import DoneIcon from "@mui/icons-material/Done"; | import DoneIcon from "@mui/icons-material/Done"; | ||||
import { getCustomWidth } from "@/app/utils/commonUtil"; | |||||
import { downloadFile, getCustomWidth } from "@/app/utils/commonUtil"; | |||||
import PoInfoCard from "./PoInfoCard"; | import PoInfoCard from "./PoInfoCard"; | ||||
import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; | import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; | ||||
import { fetchPoListClient } from "@/app/api/po/actions"; | |||||
import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material"; | import { List, ListItem, ListItemButton, ListItemText, Divider } from "@mui/material"; | ||||
import { createStockInLine } from "@/app/api/dashboard/actions"; | |||||
import { Controller, FormProvider, useForm } from "react-hook-form"; | import { Controller, FormProvider, useForm } from "react-hook-form"; | ||||
import dayjs, { Dayjs } from "dayjs"; | import dayjs, { Dayjs } from "dayjs"; | ||||
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | ||||
import { DatePicker, LocalizationProvider, zhHK } from "@mui/x-date-pickers"; | import { DatePicker, LocalizationProvider, zhHK } from "@mui/x-date-pickers"; | ||||
import { debounce } from "lodash"; | import { debounce } from "lodash"; | ||||
import LoadingComponent from "../General/LoadingComponent"; | |||||
import { getMailTemplateForStockInLine } from "@/app/api/mailTemplate/actions"; | |||||
//import { useRouter } from "next/navigation"; | //import { useRouter } from "next/navigation"; | ||||
type Props = { | type Props = { | ||||
po: PoResult; | po: PoResult; | ||||
qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
@@ -115,7 +116,7 @@ const PoSearchList: React.FC<{ | |||||
return ( | return ( | ||||
<Paper sx={{ p: 2, maxHeight: "480px", overflow: "auto", minWidth: "300px", height: "480px" }}> | <Paper sx={{ p: 2, maxHeight: "480px", overflow: "auto", minWidth: "300px", height: "480px" }}> | ||||
<Typography variant="h6" gutterBottom> | <Typography variant="h6" gutterBottom> | ||||
{t("Purchase Orders")} | |||||
{t("Purchase Order")} | |||||
</Typography> | </Typography> | ||||
<TextField | <TextField | ||||
label={t("Search")} | label={t("Search")} | ||||
@@ -133,44 +134,49 @@ const PoSearchList: React.FC<{ | |||||
), | ), | ||||
}} | }} | ||||
/> | /> | ||||
<List dense sx={{ width: '100%' }}> | |||||
{filteredPoList.map((poItem, index) => ( | |||||
<div key={poItem.id}> | |||||
<ListItem disablePadding sx={{ width: '100%' }}> | |||||
<ListItemButton | |||||
selected={selectedPoId === poItem.id} | |||||
onClick={() => onSelect(poItem)} | |||||
sx={{ | |||||
width: '100%', | |||||
"&.Mui-selected": { | |||||
backgroundColor: "primary.light", | |||||
"&:hover": { | |||||
{(filteredPoList.length > 0)? ( | |||||
<List dense sx={{ width: '100%' }}> | |||||
{filteredPoList.map((poItem, index) => ( | |||||
<div key={poItem.id}> | |||||
<ListItem disablePadding sx={{ width: '100%' }}> | |||||
<ListItemButton | |||||
selected={selectedPoId === poItem.id} | |||||
onClick={() => onSelect(poItem)} | |||||
sx={{ | |||||
width: '100%', | |||||
"&.Mui-selected": { | |||||
backgroundColor: "primary.light", | backgroundColor: "primary.light", | ||||
"&:hover": { | |||||
backgroundColor: "primary.light", | |||||
}, | |||||
}, | }, | ||||
}, | |||||
}} | |||||
> | |||||
<ListItemText | |||||
primary={ | |||||
<Typography variant="body2" sx={{ wordBreak: 'break-all' }}> | |||||
{poItem.code} | |||||
</Typography> | |||||
} | |||||
secondary={ | |||||
<Typography variant="caption" color="text.secondary"> | |||||
{t(`${poItem.status.toLowerCase()}`)} | |||||
</Typography> | |||||
} | |||||
/> | |||||
</ListItemButton> | |||||
</ListItem> | |||||
{index < filteredPoList.length - 1 && <Divider />} | |||||
</div> | |||||
))} | |||||
</List> | |||||
}} | |||||
> | |||||
<ListItemText | |||||
primary={ | |||||
<Typography variant="body2" sx={{ wordBreak: 'break-all' }}> | |||||
{poItem.code} | |||||
</Typography> | |||||
} | |||||
secondary={ | |||||
<Typography variant="caption" color="text.secondary"> | |||||
{t(`${poItem.status.toLowerCase()}`)} | |||||
</Typography> | |||||
} | |||||
/> | |||||
</ListItemButton> | |||||
</ListItem> | |||||
{index < filteredPoList.length - 1 && <Divider />} | |||||
</div> | |||||
))} | |||||
</List>) : ( | |||||
<LoadingComponent/> | |||||
) | |||||
} | |||||
{searchTerm && ( | {searchTerm && ( | ||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: "block" }}> | <Typography variant="caption" color="text.secondary" sx={{ mt: 1, display: "block" }}> | ||||
{t("Found")} {filteredPoList.length} {t("of")} {poList.length} {t("items")} | |||||
{`${t("Found")} ${filteredPoList.length} ${t("Purchase Order")}`} | |||||
{/* {`${t("Found")} ${filteredPoList.length} of ${poList.length} ${t("Item")}`} */} | |||||
</Typography> | </Typography> | ||||
)} | )} | ||||
</Paper> | </Paper> | ||||
@@ -184,7 +190,7 @@ interface PolInputResult { | |||||
const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | ||||
const cameras = useContext(CameraContext); | const cameras = useContext(CameraContext); | ||||
console.log(cameras); | |||||
// console.log(cameras); | |||||
const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
const [purchaseOrder, setPurchaseOrder] = useState({ ...po }); | const [purchaseOrder, setPurchaseOrder] = useState({ ...po }); | ||||
@@ -205,6 +211,12 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
const searchParams = useSearchParams(); | const searchParams = useSearchParams(); | ||||
const [selectedRow, setSelectedRow] = useState<PurchaseOrderLine | null>(null); | const [selectedRow, setSelectedRow] = useState<PurchaseOrderLine | null>(null); | ||||
const defaultPolId = searchParams.get("polId") | |||||
useEffect(() => { | |||||
if (defaultPolId) { | |||||
setSelectedRow(rows.find((r) => r.id.toString() === defaultPolId) ?? null) | |||||
} | |||||
}, []) | |||||
const [stockInLine, setStockInLine] = useState<StockInLine[]>([]); | const [stockInLine, setStockInLine] = useState<StockInLine[]>([]); | ||||
const [processedQty, setProcessedQty] = useState(0); | const [processedQty, setProcessedQty] = useState(0); | ||||
@@ -212,6 +224,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
const router = useRouter(); | const router = useRouter(); | ||||
const [poList, setPoList] = useState<PoResult[]>([]); | const [poList, setPoList] = useState<PoResult[]>([]); | ||||
const [selectedPoId, setSelectedPoId] = useState(po.id); | const [selectedPoId, setSelectedPoId] = useState(po.id); | ||||
const [focusField, setFocusField] = useState<HTMLInputElement>(); | |||||
const currentPoId = searchParams.get('id'); | const currentPoId = searchParams.get('id'); | ||||
const selectedIdsParam = searchParams.get('selectedIds'); | const selectedIdsParam = searchParams.get('selectedIds'); | ||||
// const [selectedRowId, setSelectedRowId] = useState<number | null>(null); | // const [selectedRowId, setSelectedRowId] = useState<number | null>(null); | ||||
@@ -261,6 +275,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
setProcessedQty(result.pol[0].processed); | setProcessedQty(result.pol[0].processed); | ||||
} | } | ||||
} | } | ||||
// if (focusField) {console.log(focusField);focusField.focus();} | |||||
} | } | ||||
} catch (error) { | } catch (error) { | ||||
console.error("Failed to fetch PO detail:", error); | console.error("Failed to fetch PO detail:", error); | ||||
@@ -319,6 +334,13 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
setPurchaseOrder(newPo); | setPurchaseOrder(newPo); | ||||
}, [purchaseOrder.id]); | }, [purchaseOrder.id]); | ||||
const handleMailTemplateForStockInLine = useCallback(async (stockInLineId: number) => { | |||||
const response = await getMailTemplateForStockInLine(stockInLineId) | |||||
if (response) { | |||||
downloadFile(new Uint8Array(response.blobValue), response.filename); | |||||
} | |||||
}, []) | |||||
useEffect(() => { | useEffect(() => { | ||||
setRows(purchaseOrder.pol || []); | setRows(purchaseOrder.pol || []); | ||||
}, [purchaseOrder]); | }, [purchaseOrder]); | ||||
@@ -388,6 +410,9 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
setTimeout(async () => { | setTimeout(async () => { | ||||
// post stock in line | // post stock in line | ||||
const oldId = row.id; | const oldId = row.id; | ||||
const acceptedQty = Number(polInputList[rowIndex].dnQty); | |||||
if (isNaN(acceptedQty) || acceptedQty <= 0) { alert("來貨數量必須大於0!"); return; } // Temp check, need update | |||||
const postData = { | const postData = { | ||||
dnNo: dnFormProps.watch("dnNo"), | dnNo: dnFormProps.watch("dnNo"), | ||||
dnDate: outputDateStringToInputDateString(dnFormProps.watch("dnDate")), | dnDate: outputDateStringToInputDateString(dnFormProps.watch("dnDate")), | ||||
@@ -396,7 +421,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
itemName: row.itemName, | itemName: row.itemName, | ||||
purchaseOrderId: row.purchaseOrderId, | purchaseOrderId: row.purchaseOrderId, | ||||
purchaseOrderLineId: row.id, | purchaseOrderLineId: row.id, | ||||
acceptedQty: polInputList[rowIndex].dnQty || 0, | |||||
acceptedQty: acceptedQty, | |||||
productLotNo: polInputList[rowIndex].lotNo || '', | productLotNo: polInputList[rowIndex].lotNo || '', | ||||
// acceptedQty: secondReceiveQty || 0, | // acceptedQty: secondReceiveQty || 0, | ||||
// acceptedQty: row.acceptedQty, | // acceptedQty: row.acceptedQty, | ||||
@@ -447,6 +472,10 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
} | } | ||||
// setPolInputList(() => temp) | // setPolInputList(() => temp) | ||||
}, 300), [rowIndex]); | }, 300), [rowIndex]); | ||||
// const [focusField, setFocusField] = useState<HTMLInputElement>(); | |||||
const purchaseToStockRatio = (row.stockUom.purchaseRatioN ?? 1) / (row.stockUom.purchaseRatioD ?? 1) * (row.stockUom.stockRatioD ?? 1) / (row.stockUom.stockRatioN ?? 1) | |||||
return ( | return ( | ||||
<> | <> | ||||
<TableRow | <TableRow | ||||
@@ -477,7 +506,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
<TableCell align="right">{integerFormatter.format(row.qty)}</TableCell> | <TableCell align="right">{integerFormatter.format(row.qty)}</TableCell> | ||||
<TableCell align="right">{integerFormatter.format(processedQty)}</TableCell> | <TableCell align="right">{integerFormatter.format(processedQty)}</TableCell> | ||||
<TableCell align="left">{row.uom?.code}</TableCell> | <TableCell align="left">{row.uom?.code}</TableCell> | ||||
<TableCell align="right">{decimalFormatter.format(row.stockUom.stockQty)}</TableCell> | |||||
{/* <TableCell align="right">{decimalFormatter.format(row.stockUom.stockQty)}</TableCell> */} | |||||
<TableCell align="right">{decimalFormatter.format(row.stockInLine.filter((sil) => sil.purchaseOrderLineId === row.id).reduce((acc, cur) => acc + (cur.acceptedQty ?? 0),0) * purchaseToStockRatio)}</TableCell> | |||||
<TableCell align="left">{row.stockUom.stockUomCode}</TableCell> | <TableCell align="left">{row.stockUom.stockUomCode}</TableCell> | ||||
{/* <TableCell align="right"> | {/* <TableCell align="right"> | ||||
{decimalFormatter.format(totalWeight)} {weightUnit} | {decimalFormatter.format(totalWeight)} {weightUnit} | ||||
@@ -495,6 +525,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
variant="outlined" | variant="outlined" | ||||
defaultValue={polInputList[rowIndex]?.lotNo ?? ''} | defaultValue={polInputList[rowIndex]?.lotNo ?? ''} | ||||
onChange={handleChange} | onChange={handleChange} | ||||
// onFocus={(e) => {setFocusField(e.target as HTMLInputElement);}} | |||||
/> | /> | ||||
</TableCell> | </TableCell> | ||||
<TableCell align="center"> | <TableCell align="center"> | ||||
@@ -520,7 +551,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
handleStart() | handleStart() | ||||
} | } | ||||
> | > | ||||
提交 | |||||
{t("submit")} | |||||
</Button> | </Button> | ||||
</TableCell> | </TableCell> | ||||
</TableRow> | </TableRow> | ||||
@@ -554,6 +585,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
</> | </> | ||||
); | ); | ||||
} | } | ||||
// ROW END | |||||
const [tabIndex, setTabIndex] = useState(0); | const [tabIndex, setTabIndex] = useState(0); | ||||
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | ||||
(_e, newValue) => { | (_e, newValue) => { | ||||
@@ -824,6 +857,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
itemDetail={selectedRow} | itemDetail={selectedRow} | ||||
warehouse={warehouse} | warehouse={warehouse} | ||||
fetchPoDetail={fetchPoDetail} | fetchPoDetail={fetchPoDetail} | ||||
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | |||||
/> | /> | ||||
</Box> | </Box> | ||||
</TableCell> | </TableCell> | ||||
@@ -10,6 +10,7 @@ import PoDetail from "./PoDetail"; | |||||
import { QcItemWithChecks } from "@/app/api/qc"; | import { QcItemWithChecks } from "@/app/api/qc"; | ||||
import { fetchWarehouseList } from "@/app/api/warehouse"; | import { fetchWarehouseList } from "@/app/api/warehouse"; | ||||
import { fetchQcItemCheck } from "@/app/api/qc/actions"; | import { fetchQcItemCheck } from "@/app/api/qc/actions"; | ||||
import { fetchEscalationCombo } from "@/app/api/user"; | |||||
interface SubComponents { | interface SubComponents { | ||||
Loading: typeof PoDetailLoading; | Loading: typeof PoDetailLoading; | ||||
@@ -20,10 +21,16 @@ type Props = { | |||||
}; | }; | ||||
const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => { | const PoDetailWrapper: React.FC<Props> & SubComponents = async ({ id }) => { | ||||
const [poWithStockInLine, warehouse, qc] = await Promise.all([ | |||||
const [ | |||||
poWithStockInLine, | |||||
warehouse, | |||||
qc, | |||||
escalationCombo | |||||
] = await Promise.all([ | |||||
fetchPoWithStockInLines(id), | fetchPoWithStockInLines(id), | ||||
fetchWarehouseList(), | fetchWarehouseList(), | ||||
fetchQcItemCheck(), | fetchQcItemCheck(), | ||||
fetchEscalationCombo(), | |||||
]); | ]); | ||||
// const poWithStockInLine = await fetchPoWithStockInLines(id) | // const poWithStockInLine = await fetchPoWithStockInLines(id) | ||||
@@ -19,7 +19,7 @@ type Props = { | |||||
po: PoResult; | po: PoResult; | ||||
}; | }; | ||||
const PoInfoCard: React.FC<Props> = async ( | |||||
const PoInfoCard: React.FC<Props> = ( | |||||
{ | { | ||||
// id | // id | ||||
po | po | ||||
@@ -74,6 +74,7 @@ interface Props { | |||||
stockInLine: StockInLine[]; | stockInLine: StockInLine[]; | ||||
warehouse: WarehouseResult[]; | warehouse: WarehouseResult[]; | ||||
fetchPoDetail: (poId: string) => void; | fetchPoDetail: (poId: string) => void; | ||||
handleMailTemplateForStockInLine: (stockInLineId: number) => void; | |||||
} | } | ||||
export type StockInLineEntryError = { | export type StockInLineEntryError = { | ||||
@@ -112,7 +113,8 @@ function PoInputGrid({ | |||||
itemDetail, | itemDetail, | ||||
stockInLine, | stockInLine, | ||||
warehouse, | warehouse, | ||||
fetchPoDetail | |||||
fetchPoDetail, | |||||
handleMailTemplateForStockInLine | |||||
}: Props) { | }: Props) { | ||||
console.log(itemDetail); | console.log(itemDetail); | ||||
const { t } = useTranslation("purchaseOrder"); | const { t } = useTranslation("purchaseOrder"); | ||||
@@ -270,6 +272,7 @@ function PoInputGrid({ | |||||
const [newOpen, setNewOpen] = useState(false); | const [newOpen, setNewOpen] = useState(false); | ||||
const stockInLineId = searchParams.get("stockInLineId"); | const stockInLineId = searchParams.get("stockInLineId"); | ||||
const poLineId = searchParams.get("poLineId"); | |||||
const closeNewModal = useCallback(() => { | const closeNewModal = useCallback(() => { | ||||
const newParams = new URLSearchParams(searchParams.toString()); | const newParams = new URLSearchParams(searchParams.toString()); | ||||
newParams.delete("stockInLineId"); // Remove the parameter | newParams.delete("stockInLineId"); // Remove the parameter | ||||
@@ -282,46 +285,51 @@ const closeNewModal = useCallback(() => { | |||||
// Open modal | // Open modal | ||||
const openNewModal = useCallback(() => { | const openNewModal = useCallback(() => { | ||||
setNewOpen(true); | |||||
setNewOpen(() => true); | |||||
}, []); | }, []); | ||||
// Button handler to update the URL and open the modal | // Button handler to update the URL and open the modal | ||||
const handleNewQC = useCallback( | const handleNewQC = useCallback( | ||||
(id: GridRowId, params: any) => async () => { | |||||
(id: GridRowId, params: any) => async() => { | |||||
// console.log(id) | // console.log(id) | ||||
// console.log(params) | // console.log(params) | ||||
setBtnIsLoading(true); | |||||
// setBtnIsLoading(true); | |||||
setRowModesModel((prev) => ({ | setRowModesModel((prev) => ({ | ||||
...prev, | ...prev, | ||||
[id]: { mode: GridRowModes.View }, | [id]: { mode: GridRowModes.View }, | ||||
})); | })); | ||||
const qcResult = await fetchQcDefaultValue(id); | const qcResult = await fetchQcDefaultValue(id); | ||||
setModalInfo({ | |||||
setModalInfo(() => ({ | |||||
...params.row, | ...params.row, | ||||
qcResult: qcResult, | qcResult: qcResult, | ||||
receivedQty: itemDetail.receivedQty, | receivedQty: itemDetail.receivedQty, | ||||
}); | |||||
})); | |||||
setTimeout(() => { | setTimeout(() => { | ||||
const newParams = new URLSearchParams(searchParams.toString()); | const newParams = new URLSearchParams(searchParams.toString()); | ||||
newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates | newParams.set("stockInLineId", id.toString()); // Ensure `set` to avoid duplicates | ||||
router.replace(`${pathname}?${newParams.toString()}`); | router.replace(`${pathname}?${newParams.toString()}`); | ||||
console.log("hello") | |||||
// console.log("hello") | |||||
openNewModal() | openNewModal() | ||||
setBtnIsLoading(false); | |||||
// setBtnIsLoading(false); | |||||
}, 200); | }, 200); | ||||
}, | }, | ||||
[fetchQcDefaultValue, openNewModal, pathname, router, searchParams] | [fetchQcDefaultValue, openNewModal, pathname, router, searchParams] | ||||
); | ); | ||||
// Open modal if `stockInLineId` exists in the URL | // Open modal if `stockInLineId` exists in the URL | ||||
const [firstCheckForSil, setFirstCheckForSil] = useState(false) | |||||
useEffect(() => { | useEffect(() => { | ||||
if (stockInLineId) { | |||||
console.log("heeloo") | |||||
console.log(stockInLineId) | |||||
handleNewQC(stockInLineId, apiRef.current.getRow(stockInLineId)); | |||||
if (stockInLineId && itemDetail && !firstCheckForSil) { | |||||
// console.log("heeloo") | |||||
// console.log(stockInLineId) | |||||
// console.log(apiRef.current.getRow(stockInLineId)) | |||||
setFirstCheckForSil(true) | |||||
const fn = handleNewQC(stockInLineId, {row: apiRef.current.getRow(stockInLineId)}); | |||||
fn(); | |||||
} | } | ||||
}, [stockInLineId, newOpen, handleNewQC, apiRef]); | |||||
}, [stockInLineId, poLineId, itemDetail]); | |||||
const handleEscalation = useCallback( | const handleEscalation = useCallback( | ||||
(id: GridRowId, params: any) => () => { | (id: GridRowId, params: any) => () => { | ||||
// setBtnIsLoading(true); | // setBtnIsLoading(true); | ||||
@@ -580,7 +588,7 @@ const closeNewModal = useCallback(() => { | |||||
}} | }} | ||||
onClick={handleNewQC(params.row.id, params)} | onClick={handleNewQC(params.row.id, params)} | ||||
color="inherit" | color="inherit" | ||||
key="edit" | |||||
key={`edit`} | |||||
/>, | />, | ||||
<GridActionsCellItem | <GridActionsCellItem | ||||
icon={<Button | icon={<Button | ||||
@@ -589,7 +597,7 @@ const closeNewModal = useCallback(() => { | |||||
variant="contained" | variant="contained" | ||||
color="primary" | color="primary" | ||||
sx={{ width: '150px' }} | sx={{ width: '150px' }} | ||||
// onClick={formProps.handleSubmit(onSubmitEmailSupplier)} | |||||
onClick={() => handleMailTemplateForStockInLine(params.row.id as number)} | |||||
> | > | ||||
{t("email supplier")} | {t("email supplier")} | ||||
</Button>} | </Button>} | ||||
@@ -598,7 +606,7 @@ const closeNewModal = useCallback(() => { | |||||
// color: "primary.main", | // color: "primary.main", | ||||
// marginRight: 1, | // marginRight: 1, | ||||
}} | }} | ||||
onClick={handleNewQC(params.row.id, params)} | |||||
// onClick={handleNewQC(params.row.id, params)} | |||||
color="inherit" | color="inherit" | ||||
key="edit" | key="edit" | ||||
/>, | />, | ||||
@@ -908,6 +916,7 @@ const closeNewModal = useCallback(() => { | |||||
open={newOpen} | open={newOpen} | ||||
onClose={closeNewModal} | onClose={closeNewModal} | ||||
itemDetail={modalInfo} | itemDetail={modalInfo} | ||||
handleMailTemplateForStockInLine={handleMailTemplateForStockInLine} | |||||
/> | /> | ||||
</> | </> | ||||
) | ) | ||||
@@ -41,20 +41,20 @@ import { GridEditInputCell } from "@mui/x-data-grid"; | |||||
import { StockInLine } from "@/app/api/po"; | import { StockInLine } from "@/app/api/po"; | ||||
import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | ||||
import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions"; | import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions"; | ||||
import { QcItemWithChecks } from "@/app/api/qc"; | |||||
import { QcItemWithChecks, QcData } from "@/app/api/qc"; | |||||
import axios from "@/app/(main)/axios/axiosInstance"; | import axios from "@/app/(main)/axios/axiosInstance"; | ||||
import { NEXT_PUBLIC_API_URL } from "@/config/api"; | import { NEXT_PUBLIC_API_URL } from "@/config/api"; | ||||
import axiosInstance from "@/app/(main)/axios/axiosInstance"; | import axiosInstance from "@/app/(main)/axios/axiosInstance"; | ||||
import EscalationComponent from "./EscalationComponent"; | import EscalationComponent from "./EscalationComponent"; | ||||
import QcDataGrid from "./QCDatagrid"; | import QcDataGrid from "./QCDatagrid"; | ||||
import StockInFormVer2 from "./StockInFormVer2"; | import StockInFormVer2 from "./StockInFormVer2"; | ||||
import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate"; | |||||
import { ModalFormInput } from "@/app/api/dashboard/actions"; | |||||
import { dummyEscalationHistory, dummyQCData } from "./dummyQcTemplate"; | |||||
import { ModalFormInput } from "@/app/api/po/actions"; | |||||
import { escape } from "lodash"; | import { escape } from "lodash"; | ||||
import { PanoramaSharp } from "@mui/icons-material"; | import { PanoramaSharp } from "@mui/icons-material"; | ||||
interface Props { | interface Props { | ||||
itemDetail: StockInLine; | |||||
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] }; | |||||
qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
disabled: boolean; | disabled: boolean; | ||||
qcItems: QcData[] | qcItems: QcData[] | ||||
@@ -88,8 +88,10 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
const [tabIndex, setTabIndex] = useState(0); | const [tabIndex, setTabIndex] = useState(0); | ||||
const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>(); | const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>(); | ||||
const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory); | const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory); | ||||
const [qcResult, setQcResult] = useState(); | |||||
// const [qcResult, setQcResult] = useState(); | |||||
const qcAccept = watch("qcAccept"); | const qcAccept = watch("qcAccept"); | ||||
const qcResult = watch("qcResult"); | |||||
console.log(qcResult); | |||||
// const [qcAccept, setQcAccept] = useState(true); | // const [qcAccept, setQcAccept] = useState(true); | ||||
// const [qcItems, setQcItems] = useState(dummyQCData) | // const [qcItems, setQcItems] = useState(dummyQCData) | ||||
@@ -184,43 +186,45 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
const qcColumns: GridColDef[] = [ | const qcColumns: GridColDef[] = [ | ||||
{ | { | ||||
field: "qcItem", | |||||
field: "code", | |||||
headerName: t("qcItem"), | headerName: t("qcItem"), | ||||
flex: 2, | flex: 2, | ||||
renderCell: (params) => ( | renderCell: (params) => ( | ||||
<Box> | <Box> | ||||
<b>{params.value}</b><br/> | <b>{params.value}</b><br/> | ||||
{params.row.qcDescription}<br/> | |||||
{params.row.name}<br/> | |||||
</Box> | </Box> | ||||
), | ), | ||||
}, | }, | ||||
{ | { | ||||
field: 'isPassed', | |||||
field: 'qcPassed', | |||||
headerName: t("qcResult"), | headerName: t("qcResult"), | ||||
flex: 1.5, | flex: 1.5, | ||||
renderCell: (params) => { | renderCell: (params) => { | ||||
const currentValue = params.value; | |||||
const currentValue = params.row; | |||||
console.log(currentValue.row); | |||||
return ( | return ( | ||||
<FormControl> | <FormControl> | ||||
<RadioGroup | <RadioGroup | ||||
row | row | ||||
aria-labelledby="demo-radio-buttons-group-label" | aria-labelledby="demo-radio-buttons-group-label" | ||||
value={currentValue === undefined ? "" : (currentValue ? "true" : "false")} | |||||
value={currentValue.qcPassed === undefined ? "" : (currentValue.qcPassed ? "true" : "false")} | |||||
// value={currentValue.qcPassed === undefined ? (currentValue.failQty!==undefined?(currentValue.failQty==0?"true":"false"):"") : (currentValue.qcPassed ? "true" : "false")} | |||||
onChange={(e) => { | onChange={(e) => { | ||||
const value = e.target.value; | const value = e.target.value; | ||||
setQcItems((prev) => | setQcItems((prev) => | ||||
prev.map((r): QcData => (r.id === params.id ? { ...r, isPassed: value === "true" } : r)) | |||||
prev.map((r): QcData => (r.id === params.id ? { ...r, qcPassed: value === "true" } : r)) | |||||
); | ); | ||||
}} | }} | ||||
name={`isPassed-${params.id}`} | |||||
name={`qcPassed-${params.id}`} | |||||
> | > | ||||
<FormControlLabel | <FormControlLabel | ||||
value="true" | value="true" | ||||
control={<Radio />} | control={<Radio />} | ||||
label="合格" | label="合格" | ||||
disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={disabled} | |||||
sx={{ | sx={{ | ||||
color: currentValue === true ? "green" : "inherit", | |||||
color: currentValue.qcPassed === true ? "green" : "inherit", | |||||
"& .Mui-checked": {color: "green"} | "& .Mui-checked": {color: "green"} | ||||
}} | }} | ||||
/> | /> | ||||
@@ -228,9 +232,9 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
value="false" | value="false" | ||||
control={<Radio />} | control={<Radio />} | ||||
label="不合格" | label="不合格" | ||||
disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={disabled} | |||||
sx={{ | sx={{ | ||||
color: currentValue === false ? "red" : "inherit", | |||||
color: currentValue.qcPassed === false ? "red" : "inherit", | |||||
"& .Mui-checked": {color: "red"} | "& .Mui-checked": {color: "red"} | ||||
}} | }} | ||||
/> | /> | ||||
@@ -248,8 +252,8 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
<TextField | <TextField | ||||
type="number" | type="number" | ||||
size="small" | size="small" | ||||
value={!params.row.isPassed? (params.value ?? '') : '0'} | |||||
disabled={params.row.isPassed || itemDetail.status.toLowerCase() == "completed"} | |||||
value={!params.row.qcPassed? (params.value ?? '') : '0'} | |||||
disabled={params.row.qcPassed || disabled} | |||||
onChange={(e) => { | onChange={(e) => { | ||||
const v = e.target.value; | const v = e.target.value; | ||||
const next = v === '' ? undefined : Number(v); | const next = v === '' ? undefined : Number(v); | ||||
@@ -257,6 +261,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
setQcItems((prev) => | setQcItems((prev) => | ||||
prev.map((r) => (r.id === params.id ? { ...r, failQty: next } : r)) | prev.map((r) => (r.id === params.id ? { ...r, failQty: next } : r)) | ||||
); | ); | ||||
// setValue(`failQty`,failQty); | |||||
}} | }} | ||||
onClick={(e) => e.stopPropagation()} | onClick={(e) => e.stopPropagation()} | ||||
onMouseDown={(e) => e.stopPropagation()} | onMouseDown={(e) => e.stopPropagation()} | ||||
@@ -274,7 +279,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
<TextField | <TextField | ||||
size="small" | size="small" | ||||
value={params.value ?? ''} | value={params.value ?? ''} | ||||
disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={disabled} | |||||
onChange={(e) => { | onChange={(e) => { | ||||
const remarks = e.target.value; | const remarks = e.target.value; | ||||
// const next = v === '' ? undefined : Number(v); | // const next = v === '' ? undefined : Number(v); | ||||
@@ -283,6 +288,9 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
prev.map((r) => (r.id === params.id ? { ...r, remarks: remarks } : r)) | prev.map((r) => (r.id === params.id ? { ...r, remarks: remarks } : r)) | ||||
); | ); | ||||
}} | }} | ||||
// {...register(`qcResult.${params.row.rowIndex}.remarks`, { | |||||
// required: "remarks required!", | |||||
// })} | |||||
onClick={(e) => e.stopPropagation()} | onClick={(e) => e.stopPropagation()} | ||||
onMouseDown={(e) => e.stopPropagation()} | onMouseDown={(e) => e.stopPropagation()} | ||||
onKeyDown={(e) => e.stopPropagation()} | onKeyDown={(e) => e.stopPropagation()} | ||||
@@ -293,11 +301,6 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
}, | }, | ||||
] | ] | ||||
useEffect(() => { | |||||
console.log(itemDetail); | |||||
}, [itemDetail]); | |||||
// Set initial value for acceptQty | // Set initial value for acceptQty | ||||
useEffect(() => { | useEffect(() => { | ||||
if (itemDetail?.demandQty > 0) { //!== undefined) { | if (itemDetail?.demandQty > 0) { //!== undefined) { | ||||
@@ -308,10 +311,10 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
}, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]); | }, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]); | ||||
// const [openCollapse, setOpenCollapse] = useState(false) | // const [openCollapse, setOpenCollapse] = useState(false) | ||||
const [isCollapsed, setIsCollapsed] = useState<boolean>(false); | |||||
const [isCollapsed, setIsCollapsed] = useState<boolean>(true); | |||||
const onFailedOpenCollapse = useCallback((qcItems: QcData[]) => { | |||||
const isFailed = qcItems.some((qc) => !qc.isPassed) | |||||
const onFailedOpenCollapse = useCallback((qcItems: PurchaseQcResult[]) => { | |||||
const isFailed = qcItems.some((qc) => !qc.qcPassed) | |||||
console.log(isFailed) | console.log(isFailed) | ||||
if (isFailed) { | if (isFailed) { | ||||
setIsCollapsed(true) | setIsCollapsed(true) | ||||
@@ -327,10 +330,11 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
useEffect(() => { | useEffect(() => { | ||||
console.log(itemDetail); | |||||
console.log("ItemDetail in QC:", itemDetail); | |||||
}, [itemDetail]); | }, [itemDetail]); | ||||
useEffect(() => { | useEffect(() => { | ||||
// onFailedOpenCollapse(qcItems) // This function is no longer needed | // onFailedOpenCollapse(qcItems) // This function is no longer needed | ||||
}, [qcItems]); // Removed onFailedOpenCollapse from dependency array | }, [qcItems]); // Removed onFailedOpenCollapse from dependency array | ||||
@@ -366,19 +370,10 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
/> */} | /> */} | ||||
<StyledDataGrid | <StyledDataGrid | ||||
columns={qcColumns} | columns={qcColumns} | ||||
rows={qcItems} | |||||
rows={disabled? qcResult:qcItems} | |||||
autoHeight | autoHeight | ||||
/> | /> | ||||
</Grid> | </Grid> | ||||
{!qcAccept && ( | |||||
<Grid item xs={12}> | |||||
<EscalationComponent | |||||
forSupervisor={false} | |||||
isCollapsed={isCollapsed} | |||||
setIsCollapsed={setIsCollapsed} | |||||
/> | |||||
</Grid>)} | |||||
</> | </> | ||||
)} | )} | ||||
{tabIndex == 1 && ( | {tabIndex == 1 && ( | ||||
@@ -425,7 +420,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
field.onChange(value); | field.onChange(value); | ||||
}} | }} | ||||
> | > | ||||
<FormControlLabel disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
<FormControlLabel disabled={disabled} | |||||
value="true" control={<Radio />} label="接受" /> | value="true" control={<Radio />} label="接受" /> | ||||
<Box sx={{mr:2}}> | <Box sx={{mr:2}}> | ||||
<TextField | <TextField | ||||
@@ -434,7 +429,7 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
sx={{ width: '150px' }} | sx={{ width: '150px' }} | ||||
value={qcAccept? accQty : 0 } | value={qcAccept? accQty : 0 } | ||||
defaultValue={accQty} | defaultValue={accQty} | ||||
disabled={!qcAccept || itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={!qcAccept || disabled} | |||||
{...register("acceptQty", { | {...register("acceptQty", { | ||||
required: "acceptQty required!", | required: "acceptQty required!", | ||||
})} | })} | ||||
@@ -442,15 +437,27 @@ const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcI | |||||
helperText={errors.acceptQty?.message} | helperText={errors.acceptQty?.message} | ||||
/> | /> | ||||
</Box> | </Box> | ||||
<FormControlLabel disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
<FormControlLabel disabled={disabled} | |||||
value="false" control={<Radio />} | value="false" control={<Radio />} | ||||
sx={{"& .Mui-checked": {color: "red"}}} | sx={{"& .Mui-checked": {color: "red"}}} | ||||
label="不接受及上報" /> | |||||
label="不接受" /> | |||||
<FormControlLabel disabled={disabled} | |||||
value="false" control={<Radio />} | |||||
sx={{"& .Mui-checked": {color: "blue"}}} | |||||
label="上報品檢結果" /> | |||||
</RadioGroup> | </RadioGroup> | ||||
)} | )} | ||||
/> | /> | ||||
</FormControl> | </FormControl> | ||||
</Grid> | </Grid> | ||||
{!qcAccept && ( | |||||
<Grid item xs={12}> | |||||
<EscalationComponent | |||||
forSupervisor={false} | |||||
isCollapsed={isCollapsed} | |||||
setIsCollapsed={setIsCollapsed} | |||||
/> | |||||
</Grid>)} | |||||
{/* {qcAccept && <Grid item xs={12}> | {/* {qcAccept && <Grid item xs={12}> | ||||
<Typography variant="h6" display="block" marginBlockEnd={1}> | <Typography variant="h6" display="block" marginBlockEnd={1}> | ||||
{t("Escalation Result")} | {t("Escalation Result")} | ||||
@@ -1,7 +1,7 @@ | |||||
"use client"; | "use client"; | ||||
import { StockInLine } from "@/app/api/po"; | import { StockInLine } from "@/app/api/po"; | ||||
import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine } from "@/app/api/po/actions"; | |||||
import { QcItemWithChecks } from "@/app/api/qc"; | |||||
import { ModalFormInput, PurchaseQcResult, StockInLineEntry, updateStockInLine, PurchaseQCInput } from "@/app/api/po/actions"; | |||||
import { QcItemWithChecks, QcData } from "@/app/api/qc"; | |||||
import { | import { | ||||
Box, | Box, | ||||
Button, | Button, | ||||
@@ -19,10 +19,9 @@ import StockInForm from "./StockInForm"; | |||||
import StockInFormVer2 from "./StockInFormVer2"; | import StockInFormVer2 from "./StockInFormVer2"; | ||||
import QcFormVer2 from "./QcFormVer2"; | import QcFormVer2 from "./QcFormVer2"; | ||||
import PutawayForm from "./PutawayForm"; | import PutawayForm from "./PutawayForm"; | ||||
import { dummyPutawayLine, dummyQCData, QcData } from "./dummyQcTemplate"; | |||||
import { dummyPutawayLine, dummyQCData } from "./dummyQcTemplate"; | |||||
import { useGridApiRef } from "@mui/x-data-grid"; | import { useGridApiRef } from "@mui/x-data-grid"; | ||||
import {submitDialogWithWarning} from "../Swal/CustomAlerts"; | import {submitDialogWithWarning} from "../Swal/CustomAlerts"; | ||||
import { PurchaseQCInput, PutawayInput } from "@/app/api/dashboard/actions"; | |||||
import { arrayToDateString, arrayToInputDateString, dayjsToInputDateString } from "@/app/utils/formatUtil"; | import { arrayToDateString, arrayToInputDateString, dayjsToInputDateString } from "@/app/utils/formatUtil"; | ||||
import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
@@ -36,7 +35,7 @@ const style = { | |||||
px: 5, | px: 5, | ||||
pb: 10, | pb: 10, | ||||
display: "block", | display: "block", | ||||
width: { xs: "60%", sm: "60%", md: "60%" }, | |||||
width: { xs: "90%", sm: "90%", md: "90%" }, | |||||
// height: { xs: "60%", sm: "60%", md: "60%" }, | // height: { xs: "60%", sm: "60%", md: "60%" }, | ||||
}; | }; | ||||
interface CommonProps extends Omit<ModalProps, "children"> { | interface CommonProps extends Omit<ModalProps, "children"> { | ||||
@@ -55,6 +54,7 @@ interface CommonProps extends Omit<ModalProps, "children"> { | |||||
qc?: QcItemWithChecks[]; | qc?: QcItemWithChecks[]; | ||||
warehouse?: any[]; | warehouse?: any[]; | ||||
// type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | ||||
handleMailTemplateForStockInLine: (stockInLineId: number) => void; | |||||
} | } | ||||
interface Props extends CommonProps { | interface Props extends CommonProps { | ||||
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] }; | itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] }; | ||||
@@ -70,8 +70,8 @@ const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
setItemDetail, | setItemDetail, | ||||
qc, | qc, | ||||
warehouse, | warehouse, | ||||
handleMailTemplateForStockInLine, | |||||
}) => { | }) => { | ||||
console.log(warehouse); | |||||
const { | const { | ||||
t, | t, | ||||
i18n: { language }, | i18n: { language }, | ||||
@@ -113,6 +113,15 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
setOpenPutaway(isPutaway); | setOpenPutaway(isPutaway); | ||||
}, [open]) | }, [open]) | ||||
const [viewOnly, setViewOnly] = useState(false); | |||||
useEffect(() => { | |||||
if (itemDetail && itemDetail.status) { | |||||
const isViewOnly = itemDetail.status.toLowerCase() == "completed" || itemDetail.status.toLowerCase() == "rejected" | |||||
setViewOnly(isViewOnly) | |||||
} | |||||
}, [itemDetail]); | |||||
const [openPutaway, setOpenPutaway] = useState(false); | const [openPutaway, setOpenPutaway] = useState(false); | ||||
const onOpenPutaway = useCallback(() => { | const onOpenPutaway = useCallback(() => { | ||||
@@ -155,21 +164,25 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
// Get QC data from the shared form context | // Get QC data from the shared form context | ||||
const qcAccept = data.qcAccept; | const qcAccept = data.qcAccept; | ||||
const acceptQty = data.acceptQty as number; | const acceptQty = data.acceptQty as number; | ||||
const qcResults = qcItems; | |||||
// const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems; | |||||
// Validate QC data | // Validate QC data | ||||
const validationErrors : string[] = []; | const validationErrors : string[] = []; | ||||
// Check if all QC items have results | // Check if all QC items have results | ||||
const itemsWithoutResult = qcItems.filter(item => item.isPassed === undefined); | |||||
const itemsWithoutResult = qcResults.filter(item => item.qcPassed === undefined); | |||||
if (itemsWithoutResult.length > 0) { | if (itemsWithoutResult.length > 0) { | ||||
validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.qcItem).join(', ')}`); | |||||
validationErrors.push(`${t("QC items without result")}`); | |||||
// validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(', ')}`); | |||||
} | } | ||||
// Check if failed items have failed quantity | // Check if failed items have failed quantity | ||||
const failedItemsWithoutQty = qcItems.filter(item => | |||||
item.isPassed === false && (!item.failQty || item.failQty <= 0) | |||||
const failedItemsWithoutQty = qcResults.filter(item => | |||||
item.qcPassed === false && (!item.failQty || item.failQty <= 0) | |||||
); | ); | ||||
if (failedItemsWithoutQty.length > 0) { | if (failedItemsWithoutQty.length > 0) { | ||||
validationErrors.push(`${t("Failed items must have failed quantity")}: ${failedItemsWithoutQty.map(item => item.qcItem).join(', ')}`); | |||||
validationErrors.push(`${t("Failed items must have failed quantity")}`); | |||||
// validationErrors.push(`${t("Failed items must have failed quantity")}: ${failedItemsWithoutQty.map(item => item.code).join(', ')}`); | |||||
} | } | ||||
// Check if QC accept decision is made | // Check if QC accept decision is made | ||||
@@ -184,10 +197,16 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
// Check if dates are input | // Check if dates are input | ||||
if (data.productionDate === undefined || data.productionDate == null) { | if (data.productionDate === undefined || data.productionDate == null) { | ||||
validationErrors.push("Production Date cannot be null!"); | |||||
validationErrors.push("請輸入生產日期!"); | |||||
} | } | ||||
if (data.expiryDate === undefined || data.expiryDate == null) { | if (data.expiryDate === undefined || data.expiryDate == null) { | ||||
validationErrors.push("Expiry Date cannot be null!"); | |||||
validationErrors.push("請輸入到期日!"); | |||||
} | |||||
if (!qcResults.every((qc) => qc.qcPassed) && qcAccept) { | |||||
validationErrors.push("有不合格檢查項目,無法收貨!"); | |||||
// submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?", | |||||
// confirmButtonText: t("confirm putaway"), html: ""}); | |||||
// return; | |||||
} | } | ||||
if (validationErrors.length > 0) { | if (validationErrors.length > 0) { | ||||
@@ -205,12 +224,12 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
qcAccept: qcAccept? qcAccept : false, | qcAccept: qcAccept? qcAccept : false, | ||||
acceptQty: acceptQty? acceptQty : 0, | acceptQty: acceptQty? acceptQty : 0, | ||||
qcResult: qcItems.map(item => ({ | |||||
qcResult: qcResults.map(item => ({ | |||||
qcItemId: item.id, | qcItemId: item.id, | ||||
// qcItem: item.qcItem, | |||||
// code: item.code, | |||||
// qcDescription: item.qcDescription, | // qcDescription: item.qcDescription, | ||||
isPassed: item.isPassed? item.isPassed : false, | |||||
failQty: (item.failQty && !item.isPassed) ? item.failQty : 0, | |||||
qcPassed: item.qcPassed? item.qcPassed : false, | |||||
failQty: (item.failQty && !item.qcPassed) ? item.failQty : 0, | |||||
// failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0, | // failedQty: (typeof item.failedQty === "number" && !item.isPassed) ? item.failedQty : 0, | ||||
remarks: item.remarks || '' | remarks: item.remarks || '' | ||||
})) | })) | ||||
@@ -218,42 +237,21 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
// const qcData = data; | // const qcData = data; | ||||
console.log("QC Data for submission:", qcData); | console.log("QC Data for submission:", qcData); | ||||
await postStockInLine(qcData); | |||||
if (!qcData.qcResult.every((qc) => qc.isPassed) && qcData.qcAccept) { | |||||
submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?", | |||||
confirmButtonText: t("confirm putaway"), html: ""}); | |||||
return; | |||||
if (qcData.qcAccept) { | |||||
// submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?", | |||||
// confirmButtonText: t("confirm putaway"), html: ""}); | |||||
onOpenPutaway(); | |||||
} else { | |||||
closeHandler({}, "backdropClick"); | |||||
} | } | ||||
await postStockInLineWithQc(qcData); | |||||
// return; | |||||
return ; | |||||
}, | }, | ||||
[onOpenPutaway, qcItems], | [onOpenPutaway, qcItems], | ||||
); | ); | ||||
const postStockInLineWithQc = useCallback(async (qcData: PurchaseQCInput) => { | |||||
const args = { | |||||
...qcData | |||||
// id: itemDetail.id, | |||||
// purchaseOrderId: itemDetail.purchaseOrderId, | |||||
// purchaseOrderLineId: itemDetail.purchaseOrderLineId, | |||||
// itemId: itemDetail.itemId, | |||||
// ...data, | |||||
// productionDate: productionDate, | |||||
// expiryDate: expiryDate, | |||||
// receiptDate: receiptDate, | |||||
} as ModalFormInput; | |||||
await postStockInLine(args); | |||||
if (qcData.qcAccept) { | |||||
// submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?", | |||||
// confirmButtonText: t("confirm putaway"), html: ""}); | |||||
onOpenPutaway(); | |||||
} else { | |||||
closeHandler({}, "backdropClick"); | |||||
} | |||||
return ; | |||||
},[onOpenPutaway,closeHandler]); | |||||
const postStockInLine = useCallback(async (args: ModalFormInput) => { | const postStockInLine = useCallback(async (args: ModalFormInput) => { | ||||
const submitData = { | const submitData = { | ||||
@@ -329,8 +327,8 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
const acceptQty = formProps.watch("acceptedQty") | const acceptQty = formProps.watch("acceptedQty") | ||||
const checkQcIsPassed = useCallback((qcItems: QcData[]) => { | |||||
const isPassed = qcItems.every((qc) => qc.isPassed); | |||||
const checkQcIsPassed = useCallback((qcItems: PurchaseQcResult[]) => { | |||||
const isPassed = qcItems.every((qc) => qc.qcPassed); | |||||
console.log(isPassed) | console.log(isPassed) | ||||
if (isPassed) { | if (isPassed) { | ||||
formProps.setValue("passingQty", acceptQty) | formProps.setValue("passingQty", acceptQty) | ||||
@@ -343,7 +341,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
useEffect(() => { | useEffect(() => { | ||||
// maybe check if submitted before | // maybe check if submitted before | ||||
console.log(qcItems) | console.log(qcItems) | ||||
checkQcIsPassed(qcItems) | |||||
// checkQcIsPassed(qcItems) | |||||
}, [qcItems, checkQcIsPassed]) | }, [qcItems, checkQcIsPassed]) | ||||
return ( | return ( | ||||
@@ -368,7 +366,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
<PutawayForm | <PutawayForm | ||||
itemDetail={itemDetail} | itemDetail={itemDetail} | ||||
warehouse={warehouse!} | warehouse={warehouse!} | ||||
disabled={false} | |||||
disabled={viewOnly} | |||||
/> | /> | ||||
<Stack direction="row" justifyContent="flex-end" gap={1}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
<Button | <Button | ||||
@@ -406,7 +404,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
</Typography> | </Typography> | ||||
</Grid> | </Grid> | ||||
<Grid item xs={12}> | <Grid item xs={12}> | ||||
<StockInFormVer2 itemDetail={itemDetail} disabled={false} /> | |||||
<StockInFormVer2 itemDetail={itemDetail} disabled={viewOnly} /> | |||||
</Grid> | </Grid> | ||||
</Grid> | </Grid> | ||||
{/* <Stack direction="row" justifyContent="flex-end" gap={1}> | {/* <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
@@ -428,13 +426,13 @@ const [qcItems, setQcItems] = useState(dummyQCData) | |||||
<QcFormVer2 | <QcFormVer2 | ||||
qc={qc!} | qc={qc!} | ||||
itemDetail={itemDetail} | itemDetail={itemDetail} | ||||
disabled={false} | |||||
disabled={viewOnly} | |||||
qcItems={qcItems} | qcItems={qcItems} | ||||
setQcItems={setQcItems} | setQcItems={setQcItems} | ||||
/> | /> | ||||
</Grid> | </Grid> | ||||
<Stack direction="row" justifyContent="flex-end" gap={1}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
{itemDetail.status.toLowerCase() != "completed" && (<Button | |||||
{!viewOnly && (<Button | |||||
id="qcSubmit" | id="qcSubmit" | ||||
type="button" | type="button" | ||||
variant="contained" | variant="contained" | ||||
@@ -123,7 +123,7 @@ const StockInFormVer2: React.FC<Props> = ({ | |||||
{...register("dnNo", { | {...register("dnNo", { | ||||
// required: "productLotNo required!", | // required: "productLotNo required!", | ||||
})} | })} | ||||
disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={disabled} | |||||
// error={Boolean(errors.productLotNo)} | // error={Boolean(errors.productLotNo)} | ||||
// helperText={errors.productLotNo?.message} | // helperText={errors.productLotNo?.message} | ||||
/> | /> | ||||
@@ -205,7 +205,7 @@ const StockInFormVer2: React.FC<Props> = ({ | |||||
{...register("productLotNo", { | {...register("productLotNo", { | ||||
// required: "productLotNo required!", | // required: "productLotNo required!", | ||||
})} | })} | ||||
disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={disabled} | |||||
error={Boolean(errors.productLotNo)} | error={Boolean(errors.productLotNo)} | ||||
helperText={errors.productLotNo?.message} | helperText={errors.productLotNo?.message} | ||||
/> | /> | ||||
@@ -226,7 +226,7 @@ const StockInFormVer2: React.FC<Props> = ({ | |||||
sx={{ width: "100%" }} | sx={{ width: "100%" }} | ||||
label={t("productionDate")} | label={t("productionDate")} | ||||
value={productionDate ? dayjs(productionDate) : undefined} | value={productionDate ? dayjs(productionDate) : undefined} | ||||
disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={disabled} | |||||
onChange={(date) => { | onChange={(date) => { | ||||
if (!date) return; | if (!date) return; | ||||
setValue( | setValue( | ||||
@@ -275,7 +275,7 @@ const StockInFormVer2: React.FC<Props> = ({ | |||||
sx={{ width: "100%" }} | sx={{ width: "100%" }} | ||||
label={t("expiryDate")} | label={t("expiryDate")} | ||||
value={expiryDate ? dayjs(expiryDate) : undefined} | value={expiryDate ? dayjs(expiryDate) : undefined} | ||||
disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={disabled} | |||||
onChange={(date) => { | onChange={(date) => { | ||||
console.log(date); | console.log(date); | ||||
if (!date) return; | if (!date) return; | ||||
@@ -311,10 +311,10 @@ const StockInFormVer2: React.FC<Props> = ({ | |||||
<TextField | <TextField | ||||
label={t("uom")} | label={t("uom")} | ||||
fullWidth | fullWidth | ||||
{...register("uom", { | |||||
{...register("uom.code", { | |||||
required: "uom required!", | required: "uom required!", | ||||
})} | })} | ||||
value={uom.code} | |||||
// value={uom?.code} | |||||
disabled={true} | disabled={true} | ||||
/> | /> | ||||
</Grid> | </Grid> | ||||
@@ -322,7 +322,7 @@ const StockInFormVer2: React.FC<Props> = ({ | |||||
<TextField | <TextField | ||||
label={t("acceptedQty")} | label={t("acceptedQty")} | ||||
fullWidth | fullWidth | ||||
disabled={itemDetail.status.toLowerCase() == "completed"} | |||||
disabled={disabled} | |||||
{...register("acceptedQty", { | {...register("acceptedQty", { | ||||
required: "acceptedQty required!", | required: "acceptedQty required!", | ||||
})} | })} | ||||
@@ -1,52 +1,58 @@ | |||||
import { PutawayLine } from "@/app/api/po/actions" | import { PutawayLine } from "@/app/api/po/actions" | ||||
import { QcData } from "@/app/api/qc" | |||||
export interface QcData { | |||||
id: number, | |||||
qcItem: string, | |||||
qcDescription: string, | |||||
isPassed: boolean | undefined | |||||
failQty: number | undefined | |||||
remarks: string | undefined | |||||
} | |||||
// export interface QcData { | |||||
// qcItemId: number, | |||||
// qcItem: string, | |||||
// qcDescription: string, | |||||
// isPassed: boolean | undefined | |||||
// failQty: number | undefined | |||||
// remarks: string | undefined | |||||
// } | |||||
export const dummyQCData: QcData[] = [ | export const dummyQCData: QcData[] = [ | ||||
{ | { | ||||
id: 1, | |||||
qcItem: "包裝", | |||||
id: 4, | |||||
code: "包裝", | |||||
qcDescription: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格", | qcDescription: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格", | ||||
isPassed: undefined, | |||||
name: "有破爛、污糟、脹袋、積水、與實物不符等任何一種情況,則不合格", | |||||
qcPassed: undefined, | |||||
failQty: undefined, | failQty: undefined, | ||||
remarks: undefined, | remarks: undefined, | ||||
}, | }, | ||||
{ | { | ||||
id: 2, | |||||
qcItem: "肉質", | |||||
id: 5, | |||||
code: "肉質", | |||||
qcDescription: "肉質鬆散,則不合格", | qcDescription: "肉質鬆散,則不合格", | ||||
isPassed: undefined, | |||||
name: "肉質鬆散,則不合格", | |||||
qcPassed: undefined, | |||||
failQty: undefined, | failQty: undefined, | ||||
remarks: undefined, | remarks: undefined, | ||||
}, | }, | ||||
{ | { | ||||
id: 3, | |||||
qcItem: "顔色", | |||||
qcDescription: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,", | |||||
isPassed: undefined, | |||||
id: 6, | |||||
code: "顔色", | |||||
qcDescription: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,則不合格", | |||||
name: "不是食材應有的顔色、顔色不均匀、出現其他顔色、腌料/醬顔色不均匀,油脂部分變綠色、黃色,則不合格", | |||||
qcPassed: undefined, | |||||
failQty: undefined, | failQty: undefined, | ||||
remarks: undefined, | remarks: undefined, | ||||
}, | }, | ||||
{ | { | ||||
id: 4, | |||||
qcItem: "狀態", | |||||
id: 7, | |||||
code: "狀態", | |||||
qcDescription: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格", | qcDescription: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格", | ||||
isPassed: undefined, | |||||
name: "有結晶、結霜、解凍跡象、發霉、散發異味等任何一種情況,則不合格", | |||||
qcPassed: undefined, | |||||
failQty: undefined, | failQty: undefined, | ||||
remarks: undefined, | remarks: undefined, | ||||
}, | }, | ||||
{ | { | ||||
id: 5, | |||||
qcItem: "異物", | |||||
id: 8, | |||||
code: "異物", | |||||
qcDescription: "有不屬於本食材的雜質,則不合格", | qcDescription: "有不屬於本食材的雜質,則不合格", | ||||
isPassed: undefined, | |||||
name: "有不屬於本食材的雜質,則不合格", | |||||
qcPassed: undefined, | |||||
failQty: undefined, | failQty: undefined, | ||||
remarks: undefined, | remarks: undefined, | ||||
}, | }, | ||||
@@ -40,7 +40,7 @@ | |||||
"non-consumables": "非消耗品", | "non-consumables": "非消耗品", | ||||
"fg": "成品", | "fg": "成品", | ||||
"sfg": "半成品", | "sfg": "半成品", | ||||
"item": "物品", | |||||
"item": "貨品", | |||||
"FG":"成品", | "FG":"成品", | ||||
"FG & Material Demand Forecast Detail":"成品及材料需求預測詳情", | "FG & Material Demand Forecast Detail":"成品及材料需求預測詳情", | ||||
"View item In-out And inventory Ledger":"查看物料出入庫及庫存日誌", | "View item In-out And inventory Ledger":"查看物料出入庫及庫存日誌", | ||||
@@ -1,39 +1,46 @@ | |||||
{ | { | ||||
"Dashboard": "資訊展示面板", | |||||
"Order status": "訂單狀態", | |||||
"pending": "未收貨", | |||||
"receiving": "收貨中", | |||||
"total": "未完成總數", | |||||
"Warehouse temperature record": "倉庫溫度記錄", | |||||
"Warehouse type": "倉庫類型", | |||||
"Last 6 hours": "過去6小時", | |||||
"Last 24 hours": "過去24小時", | |||||
"Cold storage": "冷藏倉", | |||||
"Normal temperature storage": "常溫倉", | |||||
"Temperature status": "溫度狀態", | |||||
"Humidity status": "濕度狀態", | |||||
"Warehouse status": "倉庫狀態", | |||||
"Progress chart": "進度圖表", | |||||
"Purchase Order Code": "採購單號", | |||||
"Item Name": "貨品名稱", | |||||
"Escalation Level": "上報等級", | |||||
"Reason": "原因", | |||||
"Order completion": "訂單完成度", | |||||
"Raw material": "原料", | |||||
"Consumable": "消耗品", | |||||
"Shipment": "出貨", | |||||
"Extracted order": "已提取提料單", | |||||
"Pending order": "待提取提料單", | |||||
"Temperature": "溫度", | |||||
"Humidity": "濕度", | |||||
"Pending storage": "待入倉物料", | |||||
"Total storage": "已入倉物料", | |||||
"Application completion": "提料申請完成度", | |||||
"Processed application": "已處理提料申請", | |||||
"Pending application": "待處理提料申請", | |||||
"pending inspection material": "待品檢物料", | |||||
"inspected material": "已品檢物料", | |||||
"total material": "物料總數", | |||||
"stock in escalation list": "收貨已上報列表" | |||||
} | |||||
"Dashboard": "資訊展示面板", | |||||
"Order status": "訂單狀態", | |||||
"pending": "未收貨", | |||||
"receiving": "收貨中", | |||||
"total": "未完成總數", | |||||
"Warehouse temperature record": "倉庫溫度記錄", | |||||
"Warehouse type": "倉庫類型", | |||||
"Last 6 hours": "過去6小時", | |||||
"Last 24 hours": "過去24小時", | |||||
"Cold storage": "冷藏倉", | |||||
"Normal temperature storage": "常溫倉", | |||||
"Temperature status": "溫度狀態", | |||||
"Humidity status": "濕度狀態", | |||||
"Warehouse status": "倉庫狀態", | |||||
"Progress chart": "進度圖表", | |||||
"Purchase Order Code": "採購單號", | |||||
"Item Name": "貨品名稱", | |||||
"Escalation Level": "上報等級", | |||||
"Reason": "原因", | |||||
"Order completion": "訂單完成度", | |||||
"Raw material": "原料", | |||||
"Consumable": "消耗品", | |||||
"Shipment": "出貨", | |||||
"Extracted order": "已提取提料單", | |||||
"Pending order": "待提取提料單", | |||||
"Temperature": "溫度", | |||||
"Humidity": "濕度", | |||||
"Pending storage": "待入倉物料", | |||||
"Total storage": "已入倉物料", | |||||
"Application completion": "提料申請完成度", | |||||
"Processed application": "已處理提料申請", | |||||
"Pending application": "待處理提料申請", | |||||
"pending inspection material": "待品檢物料", | |||||
"inspected material": "已品檢物料", | |||||
"total material": "物料總數", | |||||
"stock in escalation list": "收貨已上報列表", | |||||
"Responsible for handling colleagues": "負責處理同事", | |||||
"Completed QC Total": "品檢完成數量", | |||||
"QC Fail Count": "品檢不合格數量", | |||||
"DN Date": "送貨日期", | |||||
"Received Qty": "收貨數量", | |||||
"Escalation List": "上報列表", | |||||
"Purchase UoM": "計量單位", | |||||
"QC Completed Count": "品檢完成數量" | |||||
} |
@@ -15,6 +15,6 @@ | |||||
"Base UoM": "基本單位", | "Base UoM": "基本單位", | ||||
"Lot No": "批號", | "Lot No": "批號", | ||||
"Expiry Date": "到期日", | "Expiry Date": "到期日", | ||||
"No items are selected yet.": "未選擇項目", | |||||
"Item selected": "已選擇項目" | |||||
"No items are selected yet.": "未選擇貨品", | |||||
"Item selected": "已選擇貨品" | |||||
} | } |
@@ -1,147 +1,135 @@ | |||||
{ | { | ||||
"Purchase Order": "採購訂單", | |||||
"Purchase Receipt": "處理採購來貨", | |||||
"Code": "編號", | |||||
"OrderDate": "下單日期", | |||||
"Order Date": "下單日期", | |||||
"Order Date To": "下單日期至", | |||||
"ETA": "預計到貨日期", | |||||
"ETA To": "預計到貨日期至", | |||||
"Details": "詳情", | |||||
"Supplier": "供應商", | |||||
"Status": "來貨狀態", | |||||
"Escalated": "上報狀態", | |||||
"NotEscalated": "無上報", | |||||
"Do you want to start?": "確定開始嗎?", | |||||
"Start": "開始", | |||||
"Start Success": "開始成功", | |||||
"Start Fail": "開始失敗", | |||||
"Start PO": "開始採購訂單", | |||||
"Do you want to complete?": "確定完成嗎?", | |||||
"Cancel": "取消", | |||||
"Complete": "完成", | |||||
"Complete Success": "完成成功", | |||||
"Complete Fail": "完成失敗", | |||||
"Complete PO": "完成採購訂單", | |||||
"General": "一般", | |||||
"Bind Storage": "綁定倉位", | |||||
"Po No.": "採購訂單編號", | |||||
"PO No.": "採購訂單編號", | |||||
"itemNo": "貨品編號", | |||||
"itemName": "貨品名稱", | |||||
"Item Detail": "貨品詳情", | |||||
"Item Code": "貨品編號", | |||||
"Item Name": "貨品名稱", | |||||
"Item Qty": "貨品數量", | |||||
"Item Accepted Qty": "貨品已收貨數量", | |||||
"Item Purchase UoM": "貨品計量單位", | |||||
"qty": "訂單數量", | |||||
"uom": "計量單位", | |||||
"Stock UoM": "庫存單位", | |||||
"Stock In Qty": "收貨數量", | |||||
"total weight": "總重量", | |||||
"weight unit": "重量單位", | |||||
"price": "訂單貨值", | |||||
"processed": "已上架數量", | |||||
"expiryDate": "到期日", | |||||
"acceptedQty": "是次來貨數量", | |||||
"putawayQty": "上架數量", | |||||
"acceptQty": "揀收數量", | |||||
"printQty": "列印數量", | |||||
"qcResult": "品檢結果", | |||||
"weight": "重量", | |||||
"start": "開始", | |||||
"qc": "質量控制", | |||||
"escalation": "上報", | |||||
"stock in": "入庫", | |||||
"putaway": "上架", | |||||
"delete": "刪除", | |||||
"Accept quantity must be greater than 0": "揀收數量不能少於1", | |||||
"QC items without result": "請完成品檢結果", | |||||
"Failed items must have failed quantity": "請輸入不合格數量", | |||||
"qty cannot be greater than remaining qty": "數量不能大於剩餘數量", | |||||
"acceptQty must not greater than": "揀收數量不能大於", | |||||
"Record pol": "記錄採購訂單", | |||||
"Add some entries!": "添加條目!", | |||||
"draft": "草稿", | |||||
"pending": "待處理", | |||||
"determine1": "上報1", | |||||
"determine2": "上報2", | |||||
"determine3": "上報3", | |||||
"receiving": "收貨中", | |||||
"received": "已檢收", | |||||
"completed": "已上架", | |||||
"rejected": "已拒絕及上報", | |||||
"status": "狀態", | |||||
"acceptedQty must not greater than": "接受數量不得大於", | |||||
"minimal value is 1": "最小值為1", | |||||
"value must be a number": "值必須是數字", | |||||
"qc Check": "質量控制檢查", | |||||
"Please select QC": "請選擇質量控制", | |||||
"failQty": "失敗數量", | |||||
"select qc": "選擇質量控制", | |||||
"enter a failQty": "請輸入失敗數量", | |||||
"qty too big": "數量過大", | |||||
"sampleRate": "抽樣率", | |||||
"sampleWeight": "樣本重量", | |||||
"totalWeight": "總重量", | |||||
"Escalation": "上報", | |||||
"to be processed": "待處理", | |||||
"supervisor": "管理層", | |||||
"Stock In Detail": "入庫詳情", | |||||
"productLotNo": "貨品批號", | |||||
"receiptDate": "收貨日期", | |||||
"acceptedWeight": "接受重量", | |||||
"productionDate": "生產日期", | |||||
"reportQty": "上報數量", | |||||
"Default Warehouse": "預設倉庫", | |||||
"Select warehouse": "選擇倉庫", | |||||
"Putaway Detail": "上架詳情", | |||||
"LotNo": "批號", | |||||
"Po Code": "採購訂單編號", | |||||
"No Warehouse": "沒有倉庫", | |||||
"Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", | |||||
"receivedQty": "已來貨數量", | |||||
"dnQty": "送貨單數量", | |||||
"Accept submit": "接受來貨", | |||||
"qc processing": "處理來貨及品檢", | |||||
"putaway processing": "處理來貨及上架", | |||||
"view stockin": "查看收貨及品檢", | |||||
"putaway processing": "處理來貨及上架", | |||||
"putawayBtn": "上架", | |||||
"dnNo": "送貨單編號", | |||||
"dnDate": "送貨單日期", | |||||
"submitStockIn": "更新來貨資料", | |||||
"QC Info": "品檢資料", | |||||
"Escalation History": "上報記錄", | |||||
"Escalation Info": "上報資料", | |||||
"Escalation Result": "上報結果", | |||||
"update qc info": "更新品檢資料", | |||||
"email supplier": "電郵供應商", | |||||
"confirm putaway": "確定及上架", | |||||
"confirm qc result": "確定品檢結果", | |||||
"warehouse": "倉庫", | |||||
"qcItem": "品檢項目", | |||||
"passed": "接受", | |||||
"failed": "不接受", | |||||
"failedQty": "不合格數", | |||||
"remarks": "備註", | |||||
"Reject": "拒絕", | |||||
"submit": "提交", | |||||
"print": "列印", | |||||
"bind": "綁定" | |||||
} | |||||
"Purchase Order": "採購訂單", | |||||
"Purchase Receipt": "處理採購來貨", | |||||
"Code": "編號", | |||||
"OrderDate": "下單日期", | |||||
"Order Date": "下單日期", | |||||
"Order Date To": "下單日期至", | |||||
"ETA": "預計到貨日期", | |||||
"ETA To": "預計到貨日期至", | |||||
"Details": "詳情", | |||||
"Supplier": "供應商", | |||||
"Status": "來貨狀態", | |||||
"Escalated": "上報狀態", | |||||
"NotEscalated": "無上報", | |||||
"Do you want to start?": "確定開始嗎?", | |||||
"Start": "開始", | |||||
"Start Success": "開始成功", | |||||
"Start Fail": "開始失敗", | |||||
"Start PO": "開始採購訂單", | |||||
"Do you want to complete?": "確定完成嗎?", | |||||
"Cancel": "取消", | |||||
"Complete": "完成", | |||||
"Complete Success": "完成成功", | |||||
"Complete Fail": "完成失敗", | |||||
"Complete PO": "完成採購訂單", | |||||
"General": "一般", | |||||
"Bind Storage": "綁定倉位", | |||||
"Po No.": "採購訂單編號", | |||||
"PO No.": "採購訂單編號", | |||||
"itemNo": "貨品編號", | |||||
"itemName": "貨品名稱", | |||||
"Item Detail": "貨品詳情", | |||||
"Item Code": "貨品編號", | |||||
"Item Name": "貨品名稱", | |||||
"Item Qty": "貨品數量", | |||||
"Item": "貨品", | |||||
"Item Accepted Qty": "貨品已收貨數量", | |||||
"Item Purchase UoM": "貨品計量單位", | |||||
"qty": "訂單數量", | |||||
"uom": "計量單位", | |||||
"Stock UoM": "庫存單位", | |||||
"Stock In Qty": "收貨數量", | |||||
"total weight": "總重量", | |||||
"weight unit": "重量單位", | |||||
"price": "訂單貨值", | |||||
"processed": "已上架數量", | |||||
"expiryDate": "到期日", | |||||
"acceptedQty": "是次來貨數量", | |||||
"putawayQty": "上架數量", | |||||
"acceptQty": "揀收數量", | |||||
"printQty": "列印數量", | |||||
"qcResult": "品檢結果", | |||||
"weight": "重量", | |||||
"start": "開始", | |||||
"qc": "質量控制", | |||||
"escalation": "上報", | |||||
"stock in": "入庫", | |||||
"putaway": "上架", | |||||
"delete": "刪除", | |||||
"Accept quantity must be greater than 0": "揀收數量不能少於1", | |||||
"QC items without result": "請完成品檢結果", | |||||
"Failed items must have failed quantity": "請輸入不合格數量", | |||||
"qty cannot be greater than remaining qty": "數量不能大於剩餘數量", | |||||
"acceptQty must not greater than": "揀收數量不能大於", | |||||
"Record pol": "記錄採購訂單", | |||||
"Add some entries!": "添加條目!", | |||||
"draft": "草稿", | |||||
"pending": "待處理", | |||||
"determine1": "上報1", | |||||
"determine2": "上報2", | |||||
"determine3": "上報3", | |||||
"receiving": "收貨中", | |||||
"received": "已檢收", | |||||
"completed": "已上架", | |||||
"rejected": "已拒絕及上報", | |||||
"status": "狀態", | |||||
"acceptedQty must not greater than": "接受數量不得大於", | |||||
"minimal value is 1": "最小值為1", | |||||
"value must be a number": "值必須是數字", | |||||
"qc Check": "質量控制檢查", | |||||
"Please select QC": "請選擇質量控制", | |||||
"failQty": "失敗數量", | |||||
"select qc": "選擇質量控制", | |||||
"enter a failQty": "請輸入失敗數量", | |||||
"qty too big": "數量過大", | |||||
"sampleRate": "抽樣率", | |||||
"sampleWeight": "樣本重量", | |||||
"totalWeight": "總重量", | |||||
"Escalation": "上報", | |||||
"to be processed": "待處理", | |||||
"supervisor": "管理層", | |||||
"Stock In Detail": "入庫詳情", | |||||
"productLotNo": "貨品批號", | |||||
"receiptDate": "收貨日期", | |||||
"acceptedWeight": "接受重量", | |||||
"productionDate": "生產日期", | |||||
"reportQty": "上報數量", | |||||
"Default Warehouse": "預設倉庫", | |||||
"Select warehouse": "選擇倉庫", | |||||
"Putaway Detail": "上架詳情", | |||||
"LotNo": "批號", | |||||
"Po Code": "採購訂單編號", | |||||
"No Warehouse": "沒有倉庫", | |||||
"Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", | |||||
"receivedQty": "已來貨數量", | |||||
"dnQty": "送貨單數量", | |||||
"Accept submit": "接受來貨", | |||||
"qc processing": "處理來貨及品檢", | |||||
"putaway processing": "處理來貨及上架", | |||||
"view stockin": "查看收貨及品檢", | |||||
"putawayBtn": "上架", | |||||
"dnNo": "送貨單編號", | |||||
"dnDate": "送貨單日期", | |||||
"submitStockIn": "更新來貨資料", | |||||
"QC Info": "品檢資料", | |||||
"Escalation History": "上報記錄", | |||||
"Escalation Info": "上報資料", | |||||
"Escalation Result": "上報結果", | |||||
"update qc info": "更新品檢資料", | |||||
"email supplier": "電郵供應商", | |||||
"confirm putaway": "確定及上架", | |||||
"confirm qc result": "確定品檢結果", | |||||
"warehouse": "倉庫", | |||||
"qcItem": "品檢項目", | |||||
"passed": "接受", | |||||
"failed": "不接受", | |||||
"failedQty": "不合格數", | |||||
"remarks": "備註", | |||||
"Reject": "拒絕", | |||||
"submit": "提交", | |||||
"print": "列印", | |||||
"bind": "綁定", | |||||
"Search": "搜尋", | |||||
"Found": "已找到" | |||||
} |
@@ -314,7 +314,7 @@ const components: ThemeOptions["components"] = { | |||||
styleOverrides: { | styleOverrides: { | ||||
root: { | root: { | ||||
borderBottomColor: palette.divider, | borderBottomColor: palette.divider, | ||||
padding: "1px 6px", | |||||
padding: "10px 6px", | |||||
fontSize: defaultFontSize - 2, | fontSize: defaultFontSize - 2, | ||||
// padding: "15px 16px", | // padding: "15px 16px", | ||||
// lineHeight: 1.5, | // lineHeight: 1.5, | ||||
@@ -342,6 +342,13 @@ const components: ThemeOptions["components"] = { | |||||
}, | }, | ||||
}, | }, | ||||
}, | }, | ||||
// MuiTableFooter: { | |||||
// styleOverrides: { | |||||
// root: { | |||||
// padding: "1px 6px", | |||||
// }, | |||||
// }, | |||||
// }, | |||||
MuiTextField: { | MuiTextField: { | ||||
defaultProps: { | defaultProps: { | ||||
variant: "filled", | variant: "filled", | ||||