diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index 37fdea7..5ad2aeb 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -51,7 +51,9 @@ export default async function MainLayout({ }} > - + + + {children} diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx index 3539937..cedc17f 100644 --- a/src/components/Breadcrumb/Breadcrumb.tsx +++ b/src/components/Breadcrumb/Breadcrumb.tsx @@ -5,6 +5,7 @@ import Typography from "@mui/material/Typography"; import Link from "next/link"; import MUILink from "@mui/material/Link"; import { usePathname } from "next/navigation"; +import { useTranslation } from "react-i18next"; const pathToLabelMap: { [path: string]: string } = { "": "Overview", @@ -21,11 +22,14 @@ const pathToLabelMap: { [path: string]: string } = { "/inventory": "Inventory", "/settings/importTesting": "Import Testing", "/do": "Delivery Order", + "/pickOrder": "Pick Order", + "/po": "Purchase Order", }; const Breadcrumb = () => { const pathname = usePathname(); const segments = pathname.split("/"); + const { t } = useTranslation("common") return ( @@ -36,7 +40,7 @@ const Breadcrumb = () => { if (index === segments.length - 1) { return ( - {label} + {t(label)} ); } else { @@ -48,7 +52,7 @@ const Breadcrumb = () => { component={Link} href={href || "/"} > - {label} + {t(label)} ); } diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx index 3470431..2c34365 100644 --- a/src/components/NavigationContent/NavigationContent.tsx +++ b/src/components/NavigationContent/NavigationContent.tsx @@ -54,26 +54,26 @@ const NavigationContent: React.FC = () => { label: "Pick Order", path: "/pickOrder", }, - { - icon: , - label: "Cons. Pick Order", - path: "", - }, - { - icon: , - label: "Delivery Pick Order", - path: "", - }, - { - icon: , - label: "Warehouse", - path: "", - }, - { - icon: , - label: "Location Transfer Order", - path: "", - }, + // { + // icon: , + // label: "Cons. Pick Order", + // path: "", + // }, + // { + // icon: , + // label: "Delivery Pick Order", + // path: "", + // }, + // { + // icon: , + // label: "Warehouse", + // path: "", + // }, + // { + // icon: , + // label: "Location Transfer Order", + // path: "", + // }, { icon: , label: "View item In-out And inventory Ledger", @@ -81,45 +81,45 @@ const NavigationContent: React.FC = () => { }, ], }, - { - icon: , - label: "Production", - path: "", - children: [ - { - icon: , - label: "Job Order", - path: "/production", - }, - { - icon: , - label: "Job Order Traceablity ", - path: "", - }, - { - icon: , - label: "Work Order", - path: "", - }, - { - icon: , - label: "Work Order Traceablity ", - path: "", - }, - ], - }, - { - icon: , - label: "Quality Control Log", - path: "", - children: [ - { - icon: , - label: "Quality Control Log", - path: "", - }, - ], - }, + // { + // icon: , + // label: "Production", + // path: "", + // children: [ + // { + // icon: , + // label: "Job Order", + // path: "", + // }, + // { + // icon: , + // label: "Job Order Traceablity ", + // path: "", + // }, + // { + // icon: , + // label: "Work Order", + // path: "", + // }, + // { + // icon: , + // label: "Work Order Traceablity ", + // path: "", + // }, + // ], + // }, + // { + // icon: , + // label: "Quality Control Log", + // path: "", + // children: [ + // { + // icon: , + // label: "Quality Control Log", + // path: "", + // }, + // ], + // }, { icon: , label: "Delivery", @@ -132,40 +132,40 @@ const NavigationContent: React.FC = () => { }, ], }, - { - icon: , - label: "Report", - path: "", - children: [ - { - icon: , - label: "report", - path: "", - }, - ], - }, - { - icon: , - label: "Recipe", - path: "", - children: [ - { - icon: , - label: "FG Recipe", - path: "", - }, - { - icon: , - label: "SFG Recipe", - path: "", - }, - { - icon: , - label: "Recipe", - path: "", - }, - ], - }, + // { + // icon: , + // label: "Report", + // path: "", + // children: [ + // { + // icon: , + // label: "report", + // path: "", + // }, + // ], + // }, + // { + // icon: , + // label: "Recipe", + // path: "", + // children: [ + // { + // icon: , + // label: "FG Recipe", + // path: "", + // }, + // { + // icon: , + // label: "SFG Recipe", + // path: "", + // }, + // { + // icon: , + // label: "Recipe", + // path: "", + // }, + // ], + // }, { icon: , label: "Scheduling", diff --git a/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx b/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx new file mode 100644 index 0000000..445b59a --- /dev/null +++ b/src/components/PickOrderSearch/ConsolidatedPickOrders.tsx @@ -0,0 +1,12 @@ + +interface Props { + +} + +const ConsolidatedPickOrders: React.FC = ({ + +}) => { + return <> +} + +export default ConsolidatedPickOrders; \ No newline at end of file diff --git a/src/components/PickOrderSearch/PickOrderSearch.tsx b/src/components/PickOrderSearch/PickOrderSearch.tsx index 61509d4..eb22d9b 100644 --- a/src/components/PickOrderSearch/PickOrderSearch.tsx +++ b/src/components/PickOrderSearch/PickOrderSearch.tsx @@ -8,6 +8,8 @@ import SearchResults, { Column } from "../SearchResults"; import { flatten, groupBy, intersectionWith, isEmpty, map, sortBy, sortedUniq, uniqBy, upperCase, upperFirst } from "lodash"; import { arrayToDateString, arrayToDayjs, dateStringToDayjs } from "@/app/utils/formatUtil"; import dayjs from "dayjs"; +import { Button, Grid, Stack, Tab, Tabs, TabsProps } from "@mui/material"; +import PickOrders from "./PickOrders"; interface Props { pickOrders: PickOrderResult[]; @@ -27,6 +29,14 @@ const PickOrderSearch: React.FC = ({ const [filteredPickOrders, setFilteredPickOrders] = useState(pickOrders) + const [tabIndex, setTabIndex] = useState(0); + const handleTabChange = useCallback>( + (_e, newValue) => { + setTabIndex(newValue); + }, + [], + ); + const searchCriteria: Criterion[] = useMemo(() => [ { label: t("Code"), paramName: "code", type: "text" }, { label: t("Target Date From"), label2: t("Target Date To"), paramName: "targetDate", type: "dateRange" }, @@ -43,9 +53,12 @@ const PickOrderSearch: React.FC = ({ "label") }, { - label: t("Items"), paramName: "items", type: "autocomplete", multiple: true, + label: t("Items"), paramName: "items", type: "autocomplete", // multiple: true, options: uniqBy(flatten(sortBy( - pickOrders.map((po) => po.items ? po.items.map((item) => ({ value: item.name, label: item.name, group: item.type })): []), + pickOrders.map((po) => po.items ? po.items.map((item) => ({ + value: item.name, label: item.name, + // group: item.type + })) : []), "label")), "value") }, ], [t]) @@ -54,52 +67,6 @@ const PickOrderSearch: React.FC = ({ setFilteredPickOrders(pickOrders) }, [pickOrders]) - const columns = useMemo[]>(() => [ - { - name: "code", - label: t("Code"), - }, - { - name: "consoCode", - label: t("Consolidated Code"), - renderCell: (params) => { - return params.consoCode ?? "N/A" - } - }, - { - name: "type", - label: t("type"), - renderCell: (params) => { - return upperCase(params.type) - } - }, - { - name: "items", - label: t("Items"), - renderCell: (params) => { - return params.items?.map((i) => i.name).join(", ") - } - }, - { - name: "targetDate", - label: t("Target Date"), - renderCell: (params) => { - return arrayToDateString(params.targetDate) - } - }, - { - name: "releasedBy", - label: t("Released By"), - }, - { - name: "status", - label: t("Status"), - renderCell: (params) => { - return upperFirst(params.status) - } - }, - ], [t]) - return ( <> = ({ onSearch={(query) => { setFilteredPickOrders( pickOrders.filter( - (po) =>{ + (po) => { const poTargetDateStr = arrayToDayjs(po.targetDate) // console.log(intersectionWith(po.items?.map(item => item.name), query.items)) return po.code.toLowerCase().includes(query.code.toLowerCase()) - && (isEmpty(query.targetDate) || poTargetDateStr.isSame(query.targetDate) || poTargetDateStr.isAfter(query.targetDate)) - && (isEmpty(query.targetDateTo) || poTargetDateStr.isSame(query.targetDateTo) || poTargetDateStr.isBefore(query.targetDateTo)) - && (intersectionWith(["All"], query.items).length > 0 || intersectionWith(po.items?.map(item => item.name), query.items).length > 0) - && (query.status.toLowerCase() == "all" || po.status.toLowerCase().includes(query.status.toLowerCase())) - && (query.type.toLowerCase() == "all" || po.type.toLowerCase().includes(query.type.toLowerCase())) + && (isEmpty(query.targetDate) || poTargetDateStr.isSame(query.targetDate) || poTargetDateStr.isAfter(query.targetDate)) + && (isEmpty(query.targetDateTo) || poTargetDateStr.isSame(query.targetDateTo) || poTargetDateStr.isBefore(query.targetDateTo)) + && (intersectionWith(["All"], query.items).length > 0 || intersectionWith(po.items?.map(item => item.name), query.items).length > 0) + && (query.status.toLowerCase() == "all" || po.status.toLowerCase().includes(query.status.toLowerCase())) + && (query.type.toLowerCase() == "all" || po.type.toLowerCase().includes(query.type.toLowerCase())) } ) ) }} onReset={onReset} /> - items={filteredPickOrders} columns={columns} pagingController={{ - pageNum: 0, - pageSize: 0, - totalCount: 0, - }} /> + + + + + {tabIndex === 0 && } ) } diff --git a/src/components/PickOrderSearch/PickOrders.tsx b/src/components/PickOrderSearch/PickOrders.tsx new file mode 100644 index 0000000..ea63b68 --- /dev/null +++ b/src/components/PickOrderSearch/PickOrders.tsx @@ -0,0 +1,100 @@ +import { Button, Grid } from "@mui/material"; +import SearchResults, { Column } from "../SearchResults/SearchResults"; +import { PickOrderResult } from "@/app/api/pickOrder"; +import { useTranslation } from "react-i18next"; +import { useCallback, useMemo, useState } from "react"; +import { isEmpty, upperCase, upperFirst } from "lodash"; +import { arrayToDateString } from "@/app/utils/formatUtil"; + +interface Props { + filteredPickOrders: PickOrderResult[], +} + +const PickOrders: React.FC = ({ + filteredPickOrders +}) => { + const { t } = useTranslation("pickOrder") + const [selectedRows, setSelectedRows] = useState<(string | number)[]>([]); + + const handleConsolidatedRows = useCallback(() => { + + }, [selectedRows]) + + const columns = useMemo[]>(() => [ + { + name: "id", + label: "", + type: "checkbox", + disabled: (params) => { + return !isEmpty(params.consoCode); + } + }, + { + name: "code", + label: t("Code"), + }, + { + name: "consoCode", + label: t("Consolidated Code"), + renderCell: (params) => { + return params.consoCode ?? "N/A" + } + }, + { + name: "type", + label: t("type"), + renderCell: (params) => { + return upperCase(params.type) + } + }, + { + name: "items", + label: t("Items"), + renderCell: (params) => { + return params.items?.map((i) => i.name).join(", ") + } + }, + { + name: "targetDate", + label: t("Target Date"), + renderCell: (params) => { + return arrayToDateString(params.targetDate) + } + }, + { + name: "releasedBy", + label: t("Released By"), + }, + { + name: "status", + label: t("Status"), + renderCell: (params) => { + return upperFirst(params.status) + } + }, + ], [t]) + + return ( + + + + + + items={filteredPickOrders} columns={columns} pagingController={{ + pageNum: 0, + pageSize: 0 + }} + checkboxIds={selectedRows} + setCheckboxIds={setSelectedRows} + /> + + + ) +} + +export default PickOrders; \ No newline at end of file diff --git a/src/components/SearchResults/SearchResults.tsx b/src/components/SearchResults/SearchResults.tsx index 8d41007..60a65d0 100644 --- a/src/components/SearchResults/SearchResults.tsx +++ b/src/components/SearchResults/SearchResults.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { Dispatch, SetStateAction } from "react"; +import React, { ChangeEvent, Dispatch, MouseEvent, SetStateAction, useCallback, useState } from "react"; import Paper from "@mui/material/Paper"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; @@ -14,6 +14,7 @@ import TableRow from "@mui/material/TableRow"; import IconButton, { IconButtonOwnProps } from "@mui/material/IconButton"; import { ButtonOwnProps, + Checkbox, Icon, IconOwnProps, SxProps, @@ -26,7 +27,7 @@ export interface ResultWithId { id: string | number; } -type ColumnType = "icon" | "decimal" | "integer"; +type ColumnType = "icon" | "decimal" | "integer" | "checkbox"; interface BaseColumn { name: keyof T; @@ -56,6 +57,13 @@ interface IntegerColumn extends BaseColumn { type: "integer"; } +interface CheckboxColumn extends BaseColumn { + type: "checkbox"; + disabled?: (params: T) => boolean; + // checkboxIds: readonly (string | number)[], + // setCheckboxIds: (ids: readonly (string | number)[]) => void +} + interface ColumnWithAction extends BaseColumn { onClick: (item: T) => void; buttonIcon: React.ReactNode; @@ -67,6 +75,7 @@ export type Column = | BaseColumn | IconColumn | DecimalColumn + | CheckboxColumn | ColumnWithAction; interface Props { @@ -77,9 +86,11 @@ interface Props { setPagingController?: Dispatch> - pagingController: { pageNum: number; pageSize: number;}; + }>> + pagingController: { pageNum: number; pageSize: number; }; isAutoPaging?: boolean; + checkboxIds?: (string | number)[]; + setCheckboxIds?: Dispatch>; } function isActionColumn( @@ -106,14 +117,20 @@ function isIntegerColumn( return column.type === "integer"; } +function isCheckboxColumn( + column: Column +): column is CheckboxColumn { + return column.type === "checkbox"; +} + // Icon Component Functions function convertObjectKeysToLowercase( obj: T ): object | undefined { return obj ? Object.fromEntries( - Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value]) - ) + Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value]) + ) : undefined; } @@ -144,9 +161,9 @@ function handleIconIcons( return column.icon ?? ; } -export const defaultPagingController:{ pageNum: number; pageSize: number} = { - "pageNum": 1, - "pageSize": 10, +export const defaultPagingController: { pageNum: number; pageSize: number } = { + "pageNum": 1, + "pageSize": 10, } function SearchResults({ items, @@ -155,7 +172,9 @@ function SearchResults({ pagingController, setPagingController, isAutoPaging = true, - totalCount + totalCount, + checkboxIds = [], + setCheckboxIds = undefined, }: Props) { const [page, setPage] = React.useState(0); const [rowsPerPage, setRowsPerPage] = React.useState(10); @@ -189,6 +208,28 @@ function SearchResults({ } }; + // checkbox + const handleRowClick = useCallback((event: MouseEvent, id: string | number) => { + if (setCheckboxIds) { + const selectedIndex = checkboxIds.indexOf(id); + let newSelected: (string | number)[] = []; + + if (selectedIndex === -1) { + newSelected = newSelected.concat(checkboxIds, id); + } else if (selectedIndex === 0) { + newSelected = newSelected.concat(checkboxIds.slice(1)); + } else if (selectedIndex === checkboxIds.length - 1) { + newSelected = newSelected.concat(checkboxIds.slice(0, -1)); + } else if (selectedIndex > 0) { + newSelected = newSelected.concat( + checkboxIds.slice(0, selectedIndex), + checkboxIds.slice(selectedIndex + 1), + ); + } + setCheckboxIds(newSelected); + } + }, [checkboxIds]) + const table = ( <> @@ -209,29 +250,16 @@ function SearchResults({ {isAutoPaging ? items - .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - .map((item) => { - return ( - - {columns.map((column, idx) => { - const columnName = column.name; - - return ( - - ); - })} - - ); - }) - : items.map((item) => { + .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + .map((item) => { return ( - + handleRowClick(event, item.id) : undefined} + role={setCheckboxIds ? "checkbox" : undefined} + > {columns.map((column, idx) => { const columnName = column.name; @@ -242,12 +270,33 @@ function SearchResults({ columnName={columnName} idx={idx} item={item} + checkboxIds={checkboxIds} /> ); })} ); - })} + }) + : items.map((item) => { + return ( + + {columns.map((column, idx) => { + const columnName = column.name; + + return ( + + ); + })} + + ); + })} @@ -255,8 +304,8 @@ function SearchResults({ rowsPerPageOptions={[10, 25, 100]} component="div" count={!totalCount || totalCount == 0 - ? items.length - : totalCount + ? items.length + : totalCount } // count={ // !pagingController || pagingController.totalCount == 0 @@ -280,6 +329,7 @@ interface TableCellsProps { columnName: keyof T; idx: number; item: T; + checkboxIds: (string | number)[]; } function TabelCells({ @@ -287,7 +337,10 @@ function TabelCells({ columnName, idx, item, + checkboxIds = [], }: TableCellsProps) { + const isItemSelected = checkboxIds.includes(item.id); + return ( ({ <>{decimalFormatter.format(Number(item[columnName]))} ) : isIntegerColumn(column) ? ( <>{integerFormatter.format(Number(item[columnName]))} + ) : isCheckboxColumn(column) ? ( + ) : column.renderCell ? ( column.renderCell(item) ) : (