| @@ -6,6 +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"; | |||||
| export interface PoResult { | export interface PoResult { | ||||
| id: number; | id: number; | ||||
| @@ -81,3 +82,9 @@ export const fetchPoWithStockInLines = cache(async (id: number) => { | |||||
| next: { tags: ["po"] }, | next: { tags: ["po"] }, | ||||
| }); | }); | ||||
| }); | }); | ||||
| export const fetchIqcLogByUser = cache(async () => { | |||||
| return serverFetchJson<IQCItems[]>(`${BASE_API_URL}/supervisionApprovalLog/stock-in`, { | |||||
| next: { tags: ["qcLog"] }, | |||||
| }); | |||||
| }); | |||||
| @@ -14,15 +14,27 @@ 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"; | ||||
| type Props = {}; | |||||
| import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval"; | |||||
| type Props = { | |||||
| iqc: IQCItems[] | |||||
| }; | |||||
| const DashboardPage: React.FC<Props> = ({}) => { | |||||
| const DashboardPage: React.FC<Props> = ({ | |||||
| iqc | |||||
| }) => { | |||||
| const { t } = useTranslation("dashboard"); | const { t } = useTranslation("dashboard"); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| return ( | return ( | ||||
| <ThemeProvider theme={theme}> | <ThemeProvider theme={theme}> | ||||
| <Grid container spacing={2}> | <Grid container spacing={2}> | ||||
| <Grid item xs={12}> | |||||
| <CollapsibleCard title={t("stock in escalation list")}> | |||||
| <CardContent> | |||||
| <SupervisorQcApproval items={iqc || []}/> | |||||
| </CardContent> | |||||
| </CollapsibleCard> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <CollapsibleCard title={t("Progress chart")}> | <CollapsibleCard title={t("Progress chart")}> | ||||
| <CardContent> | <CardContent> | ||||
| @@ -4,6 +4,7 @@ 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"; | |||||
| // export type SessionWithAbilities = { | // export type SessionWithAbilities = { | ||||
| // abilities: string[] | // abilities: string[] | ||||
| @@ -22,11 +23,16 @@ 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() | |||||
| ]) | |||||
| console.log(iqcLog) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Typography variant="h4">{t("Dashboard")}</Typography> | <Typography variant="h4">{t("Dashboard")}</Typography> | ||||
| <DashboardPage | <DashboardPage | ||||
| iqc={iqcLog} | |||||
| // abilities={session ? session?.abilities : []} | // abilities={session ? session?.abilities : []} | ||||
| /> | /> | ||||
| </> | </> | ||||
| @@ -0,0 +1,93 @@ | |||||
| "use client" | |||||
| import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material"; | |||||
| import { useRouter } from "next/navigation"; | |||||
| import { useCallback, useState } from "react"; | |||||
| import { usePathname } from "next/navigation"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| export type IQCItems = { | |||||
| id: number; | |||||
| poId: number; | |||||
| polId: number; | |||||
| stockInLineId: number; | |||||
| poCode: string | |||||
| itemName: string | |||||
| escalationLevel: string | |||||
| reason: string | |||||
| }; | |||||
| type Props = { | |||||
| items: IQCItems[]; | |||||
| }; | |||||
| const SupervisorQcApproval: React.FC<Props> = ({ | |||||
| items | |||||
| }) => { | |||||
| const { t } = useTranslation("dashboard"); | |||||
| const CARD_HEADER = t("stock in escalation list") | |||||
| const pathname = usePathname(); | |||||
| const router = useRouter(); | |||||
| const [selectedId, setSelectedId] = useState<number | null>(null); | |||||
| const navigateTo = useCallback( | |||||
| (item: IQCItems) => { | |||||
| setSelectedId(item.id); | |||||
| console.log(pathname) | |||||
| router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | |||||
| }, | |||||
| [router, pathname] | |||||
| ); | |||||
| const handleKeyDown = useCallback( | |||||
| (e: React.KeyboardEvent, item: IQCItems) => { | |||||
| if (e.key === 'Enter' || e.key === ' ') { | |||||
| e.preventDefault(); | |||||
| navigateTo(item); | |||||
| } | |||||
| }, | |||||
| [navigateTo] | |||||
| ); | |||||
| return ( | |||||
| <TableContainer component={Paper}> | |||||
| <Table aria-label="Two column navigable table" size="small"> | |||||
| <TableHead> | |||||
| <TableRow> | |||||
| <TableCell>{t("purchase order code")}</TableCell> | |||||
| <TableCell>{t("item name")}</TableCell> | |||||
| <TableCell>{t("escalation level")}</TableCell> | |||||
| <TableCell>{t("reason")}</TableCell> | |||||
| </TableRow> | |||||
| </TableHead> | |||||
| <TableBody> | |||||
| {items.map((item) => { | |||||
| const selected = selectedId === item.id; | |||||
| return ( | |||||
| <TableRow | |||||
| key={item.id} | |||||
| hover | |||||
| selected={selected} | |||||
| onClick={() => navigateTo(item)} | |||||
| // onKeyDown={(e) => handleKeyDown(e, item)} | |||||
| tabIndex={0} | |||||
| sx={{ cursor: 'pointer' }} | |||||
| // aria-label={`${item.name}, ${item.detail}`} | |||||
| > | |||||
| <TableCell component="th" scope="row"> | |||||
| {item.poCode} | |||||
| </TableCell> | |||||
| <TableCell>{item.itemName}</TableCell> | |||||
| <TableCell>{item.escalationLevel}</TableCell> | |||||
| <TableCell>{item.reason}</TableCell> | |||||
| </TableRow> | |||||
| ); | |||||
| })} | |||||
| </TableBody> | |||||
| </Table> | |||||
| </TableContainer> | |||||
| ); | |||||
| }; | |||||
| export default SupervisorQcApproval; | |||||
| @@ -66,9 +66,6 @@ import DoneIcon from "@mui/icons-material/Done"; | |||||
| import { getCustomWidth } from "@/app/utils/commonUtil"; | import { 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 { 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 { useRouter } from "next/navigation"; | //import { useRouter } from "next/navigation"; | ||||
| @@ -181,20 +178,13 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| purchaseOrder.pol || [], | purchaseOrder.pol || [], | ||||
| ); | ); | ||||
| const pathname = usePathname() | const pathname = usePathname() | ||||
| const router = useRouter(); | |||||
| const searchParams = useSearchParams(); | const searchParams = useSearchParams(); | ||||
| const [row, setRow] = useState(rows[0]); | const [row, setRow] = useState(rows[0]); | ||||
| const [stockInLine, setStockInLine] = useState(rows[0].stockInLine); | |||||
| const [stockInLine, setStockInLine] = useState<StockInLine[]>([]); | |||||
| const [processedQty, setProcessedQty] = useState(rows[0].processed); | const [processedQty, setProcessedQty] = useState(rows[0].processed); | ||||
| // const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status); | |||||
| //const router = useRouter(); | |||||
| const router = useRouter(); | |||||
| const [poList, setPoList] = useState<PoResult[]>([]); | const [poList, setPoList] = useState<PoResult[]>([]); | ||||
| const [selectedPoId, setSelectedPoId] = useState(po.id); | const [selectedPoId, setSelectedPoId] = useState(po.id); | ||||
| const currentPoId = searchParams.get('id'); | const currentPoId = searchParams.get('id'); | ||||
| @@ -297,14 +287,18 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| setRows(purchaseOrder.pol || []); | setRows(purchaseOrder.pol || []); | ||||
| }, [purchaseOrder]); | }, [purchaseOrder]); | ||||
| useEffect(() => { | |||||
| setStockInLine([]) | |||||
| }, []); | |||||
| function Row(props: { row: PurchaseOrderLine }) { | function Row(props: { row: PurchaseOrderLine }) { | ||||
| const { row } = props; | const { row } = props; | ||||
| const [firstReceiveQty, setFirstReceiveQty] = useState<number>() | |||||
| // const [firstReceiveQty, setFirstReceiveQty] = useState<number>() | |||||
| const [secondReceiveQty, setSecondReceiveQty] = useState<number>() | const [secondReceiveQty, setSecondReceiveQty] = useState<number>() | ||||
| const [open, setOpen] = useState(false); | |||||
| // const [open, setOpen] = useState(false); | |||||
| const [processedQty, setProcessedQty] = useState(row.processed); | const [processedQty, setProcessedQty] = useState(row.processed); | ||||
| const [currStatus, setCurrStatus] = useState(row.status); | const [currStatus, setCurrStatus] = useState(row.status); | ||||
| const [stockInLine, setStockInLine] = useState(row.stockInLine); | |||||
| // const [stockInLine, setStockInLine] = useState(row.stockInLine); | |||||
| const totalWeight = useMemo( | const totalWeight = useMemo( | ||||
| () => calculateWeight(row.qty, row.uom), | () => calculateWeight(row.qty, row.uom), | ||||
| [row.qty, row.uom], | [row.qty, row.uom], | ||||
| @@ -314,6 +308,13 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| [row.uom], | [row.uom], | ||||
| ); | ); | ||||
| useEffect(() => { | |||||
| const polId = searchParams.get("polId") != null ? parseInt(searchParams.get("polId")!) : null | |||||
| if (polId) { | |||||
| setStockInLine(rows.find((r) => r.id == polId)!.stockInLine) | |||||
| } | |||||
| }, []); | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (processedQty === row.qty) { | if (processedQty === row.qty) { | ||||
| setCurrStatus("completed".toUpperCase()); | setCurrStatus("completed".toUpperCase()); | ||||
| @@ -324,9 +325,29 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| } | } | ||||
| }, [processedQty, row.qty]); | }, [processedQty, row.qty]); | ||||
| const changeStockInLines = useCallback( | |||||
| (id: number) => { | |||||
| console.log(id) | |||||
| //rows = purchaseOrderLine | |||||
| const target = rows.find((r) => r.id === id) | |||||
| const stockInLine = target!.stockInLine | |||||
| console.log(stockInLine) | |||||
| console.log(stockInLine) | |||||
| setStockInLine(stockInLine) | |||||
| // console.log(pathname) | |||||
| // router.replace(`/po/edit?id=${item.poId}&polId=${item.polId}&stockInLineId=${item.stockInLineId}`); | |||||
| }, | |||||
| [] | |||||
| ); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <TableRow sx={{ "& > *": { borderBottom: "unset" }, color: "black" }}> | |||||
| <TableRow | |||||
| sx={{ "& > *": { borderBottom: "unset" }, | |||||
| color: "black" | |||||
| }} | |||||
| onClick={() => changeStockInLines(row.id)} | |||||
| > | |||||
| {/* <TableCell> | {/* <TableCell> | ||||
| <IconButton | <IconButton | ||||
| disabled={purchaseOrder.status.toLowerCase() === "pending"} | disabled={purchaseOrder.status.toLowerCase() === "pending"} | ||||
| @@ -490,7 +511,6 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| const renderFieldCondition = useCallback((field: "firstInQty" | "secondInQty"): boolean => { | const renderFieldCondition = useCallback((field: "firstInQty" | "secondInQty"): boolean => { | ||||
| switch (field) { | switch (field) { | ||||
| case FIRST_IN_FIELD: | case FIRST_IN_FIELD: | ||||
| return true; | return true; | ||||
| case SECOND_IN_FIELD: | case SECOND_IN_FIELD: | ||||
| return true; | return true; | ||||
| @@ -499,6 +519,14 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| } | } | ||||
| }, []); | }, []); | ||||
| useEffect(() => { | |||||
| const params = searchParams.get("polId") | |||||
| if (params) { | |||||
| const polId = parseInt(params) | |||||
| } | |||||
| }, [searchParams]) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack spacing={2}> | <Stack spacing={2}> | ||||
| @@ -278,16 +278,11 @@ const closeNewModal = useCallback(() => { | |||||
| setNewOpen(true); | setNewOpen(true); | ||||
| }, []); | }, []); | ||||
| // Open modal if `stockInLineId` exists in the URL | |||||
| useEffect(() => { | |||||
| if (stockInLineId && !newOpen) { | |||||
| openNewModal(); | |||||
| } | |||||
| }, [stockInLineId, newOpen, openNewModal]); | |||||
| // Button handler to update the URL and open the modal | // 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(params) | |||||
| setBtnIsLoading(true); | setBtnIsLoading(true); | ||||
| setRowModesModel((prev) => ({ | setRowModesModel((prev) => ({ | ||||
| ...prev, | ...prev, | ||||
| @@ -304,12 +299,21 @@ const closeNewModal = useCallback(() => { | |||||
| 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") | |||||
| openNewModal() | |||||
| setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
| }, 200); | }, 200); | ||||
| }, | }, | ||||
| [fetchQcDefaultValue, searchParams, router, pathname] | |||||
| [fetchQcDefaultValue, openNewModal, pathname, router, searchParams] | |||||
| ); | ); | ||||
| // Open modal if `stockInLineId` exists in the URL | |||||
| useEffect(() => { | |||||
| if (stockInLineId) { | |||||
| console.log("heeloo") | |||||
| console.log(stockInLineId) | |||||
| handleNewQC(stockInLineId, apiRef.current.getRow(stockInLineId)); | |||||
| } | |||||
| }, [stockInLineId, newOpen, handleNewQC, apiRef]); | |||||
| const handleEscalation = useCallback( | const handleEscalation = useCallback( | ||||
| (id: GridRowId, params: any) => () => { | (id: GridRowId, params: any) => () => { | ||||
| // setBtnIsLoading(true); | // setBtnIsLoading(true); | ||||
| @@ -281,7 +281,7 @@ function SearchResults<T extends ResultWithId>({ | |||||
| setCheckboxIds(newSelected); | setCheckboxIds(newSelected); | ||||
| } | } | ||||
| }, | }, | ||||
| [checkboxIds], | |||||
| [checkboxIds, setCheckboxIds], | |||||
| ); | ); | ||||
| const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => { | const handleSelectAllClick = (event: React.ChangeEvent<HTMLInputElement>) => { | ||||
| @@ -29,5 +29,7 @@ | |||||
| "Pending application": "待處理提料申請", | "Pending application": "待處理提料申請", | ||||
| "pending inspection material": "待品檢物料", | "pending inspection material": "待品檢物料", | ||||
| "inspected material": "已品檢物料", | "inspected material": "已品檢物料", | ||||
| "total material": "物料總數" | |||||
| "total material": "物料總數", | |||||
| "stock in escalation list": "收貨已上報列表" | |||||
| } | } | ||||