@@ -51,7 +51,9 @@ export default async function MainLayout({ | |||||
}} | }} | ||||
> | > | ||||
<Stack spacing={2}> | <Stack spacing={2}> | ||||
<Breadcrumb /> | |||||
<I18nProvider namespaces={["common"]}> | |||||
<Breadcrumb /> | |||||
</I18nProvider> | |||||
{children} | {children} | ||||
</Stack> | </Stack> | ||||
</Box> | </Box> | ||||
@@ -5,6 +5,7 @@ import Typography from "@mui/material/Typography"; | |||||
import Link from "next/link"; | import Link from "next/link"; | ||||
import MUILink from "@mui/material/Link"; | import MUILink from "@mui/material/Link"; | ||||
import { usePathname } from "next/navigation"; | import { usePathname } from "next/navigation"; | ||||
import { useTranslation } from "react-i18next"; | |||||
const pathToLabelMap: { [path: string]: string } = { | const pathToLabelMap: { [path: string]: string } = { | ||||
"": "Overview", | "": "Overview", | ||||
@@ -21,11 +22,14 @@ const pathToLabelMap: { [path: string]: string } = { | |||||
"/inventory": "Inventory", | "/inventory": "Inventory", | ||||
"/settings/importTesting": "Import Testing", | "/settings/importTesting": "Import Testing", | ||||
"/do": "Delivery Order", | "/do": "Delivery Order", | ||||
"/pickOrder": "Pick Order", | |||||
"/po": "Purchase Order", | |||||
}; | }; | ||||
const Breadcrumb = () => { | const Breadcrumb = () => { | ||||
const pathname = usePathname(); | const pathname = usePathname(); | ||||
const segments = pathname.split("/"); | const segments = pathname.split("/"); | ||||
const { t } = useTranslation("common") | |||||
return ( | return ( | ||||
<Breadcrumbs> | <Breadcrumbs> | ||||
@@ -36,7 +40,7 @@ const Breadcrumb = () => { | |||||
if (index === segments.length - 1) { | if (index === segments.length - 1) { | ||||
return ( | return ( | ||||
<Typography key={index} color="text.primary"> | <Typography key={index} color="text.primary"> | ||||
{label} | |||||
{t(label)} | |||||
</Typography> | </Typography> | ||||
); | ); | ||||
} else { | } else { | ||||
@@ -48,7 +52,7 @@ const Breadcrumb = () => { | |||||
component={Link} | component={Link} | ||||
href={href || "/"} | href={href || "/"} | ||||
> | > | ||||
{label} | |||||
{t(label)} | |||||
</MUILink> | </MUILink> | ||||
); | ); | ||||
} | } | ||||
@@ -54,26 +54,26 @@ const NavigationContent: React.FC = () => { | |||||
label: "Pick Order", | label: "Pick Order", | ||||
path: "/pickOrder", | path: "/pickOrder", | ||||
}, | }, | ||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Cons. Pick Order", | |||||
path: "", | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Delivery Pick Order", | |||||
path: "", | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Warehouse", | |||||
path: "", | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Location Transfer Order", | |||||
path: "", | |||||
}, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Cons. Pick Order", | |||||
// path: "", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Delivery Pick Order", | |||||
// path: "", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Warehouse", | |||||
// path: "", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Location Transfer Order", | |||||
// path: "", | |||||
// }, | |||||
{ | { | ||||
icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
label: "View item In-out And inventory Ledger", | label: "View item In-out And inventory Ledger", | ||||
@@ -81,45 +81,45 @@ const NavigationContent: React.FC = () => { | |||||
}, | }, | ||||
], | ], | ||||
}, | }, | ||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Production", | |||||
path: "", | |||||
children: [ | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Job Order", | |||||
path: "/production", | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Job Order Traceablity ", | |||||
path: "", | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Work Order", | |||||
path: "", | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Work Order Traceablity ", | |||||
path: "", | |||||
}, | |||||
], | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Quality Control Log", | |||||
path: "", | |||||
children: [ | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Quality Control Log", | |||||
path: "", | |||||
}, | |||||
], | |||||
}, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Production", | |||||
// path: "", | |||||
// children: [ | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Job Order", | |||||
// path: "", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Job Order Traceablity ", | |||||
// path: "", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Work Order", | |||||
// path: "", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Work Order Traceablity ", | |||||
// path: "", | |||||
// }, | |||||
// ], | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Quality Control Log", | |||||
// path: "", | |||||
// children: [ | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Quality Control Log", | |||||
// path: "", | |||||
// }, | |||||
// ], | |||||
// }, | |||||
{ | { | ||||
icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
label: "Delivery", | label: "Delivery", | ||||
@@ -132,40 +132,40 @@ const NavigationContent: React.FC = () => { | |||||
}, | }, | ||||
], | ], | ||||
}, | }, | ||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Report", | |||||
path: "", | |||||
children: [ | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "report", | |||||
path: "", | |||||
}, | |||||
], | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Recipe", | |||||
path: "", | |||||
children: [ | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "FG Recipe", | |||||
path: "", | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "SFG Recipe", | |||||
path: "", | |||||
}, | |||||
{ | |||||
icon: <RequestQuote />, | |||||
label: "Recipe", | |||||
path: "", | |||||
}, | |||||
], | |||||
}, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Report", | |||||
// path: "", | |||||
// children: [ | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "report", | |||||
// path: "", | |||||
// }, | |||||
// ], | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Recipe", | |||||
// path: "", | |||||
// children: [ | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "FG Recipe", | |||||
// path: "", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "SFG Recipe", | |||||
// path: "", | |||||
// }, | |||||
// { | |||||
// icon: <RequestQuote />, | |||||
// label: "Recipe", | |||||
// path: "", | |||||
// }, | |||||
// ], | |||||
// }, | |||||
{ | { | ||||
icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
label: "Scheduling", | label: "Scheduling", | ||||
@@ -0,0 +1,12 @@ | |||||
interface Props { | |||||
} | |||||
const ConsolidatedPickOrders: React.FC<Props> = ({ | |||||
}) => { | |||||
return <></> | |||||
} | |||||
export default ConsolidatedPickOrders; |
@@ -8,6 +8,8 @@ import SearchResults, { Column } from "../SearchResults"; | |||||
import { flatten, groupBy, intersectionWith, isEmpty, map, sortBy, sortedUniq, uniqBy, upperCase, upperFirst } from "lodash"; | import { flatten, groupBy, intersectionWith, isEmpty, map, sortBy, sortedUniq, uniqBy, upperCase, upperFirst } from "lodash"; | ||||
import { arrayToDateString, arrayToDayjs, dateStringToDayjs } from "@/app/utils/formatUtil"; | import { arrayToDateString, arrayToDayjs, dateStringToDayjs } from "@/app/utils/formatUtil"; | ||||
import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
import { Button, Grid, Stack, Tab, Tabs, TabsProps } from "@mui/material"; | |||||
import PickOrders from "./PickOrders"; | |||||
interface Props { | interface Props { | ||||
pickOrders: PickOrderResult[]; | pickOrders: PickOrderResult[]; | ||||
@@ -27,6 +29,14 @@ const PickOrderSearch: React.FC<Props> = ({ | |||||
const [filteredPickOrders, setFilteredPickOrders] = useState(pickOrders) | const [filteredPickOrders, setFilteredPickOrders] = useState(pickOrders) | ||||
const [tabIndex, setTabIndex] = useState(0); | |||||
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
(_e, newValue) => { | |||||
setTabIndex(newValue); | |||||
}, | |||||
[], | |||||
); | |||||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo(() => [ | const searchCriteria: Criterion<SearchParamNames>[] = useMemo(() => [ | ||||
{ label: t("Code"), paramName: "code", type: "text" }, | { label: t("Code"), paramName: "code", type: "text" }, | ||||
{ label: t("Target Date From"), label2: t("Target Date To"), paramName: "targetDate", type: "dateRange" }, | { label: t("Target Date From"), label2: t("Target Date To"), paramName: "targetDate", type: "dateRange" }, | ||||
@@ -43,9 +53,12 @@ const PickOrderSearch: React.FC<Props> = ({ | |||||
"label") | "label") | ||||
}, | }, | ||||
{ | { | ||||
label: t("Items"), paramName: "items", type: "autocomplete", multiple: true, | |||||
label: t("Items"), paramName: "items", type: "autocomplete", // multiple: true, | |||||
options: uniqBy(flatten(sortBy( | 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") | "label")), "value") | ||||
}, | }, | ||||
], [t]) | ], [t]) | ||||
@@ -54,52 +67,6 @@ const PickOrderSearch: React.FC<Props> = ({ | |||||
setFilteredPickOrders(pickOrders) | setFilteredPickOrders(pickOrders) | ||||
}, [pickOrders]) | }, [pickOrders]) | ||||
const columns = useMemo<Column<PickOrderResult>[]>(() => [ | |||||
{ | |||||
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 ( | return ( | ||||
<> | <> | ||||
<SearchBox | <SearchBox | ||||
@@ -107,27 +74,27 @@ const PickOrderSearch: React.FC<Props> = ({ | |||||
onSearch={(query) => { | onSearch={(query) => { | ||||
setFilteredPickOrders( | setFilteredPickOrders( | ||||
pickOrders.filter( | pickOrders.filter( | ||||
(po) =>{ | |||||
(po) => { | |||||
const poTargetDateStr = arrayToDayjs(po.targetDate) | const poTargetDateStr = arrayToDayjs(po.targetDate) | ||||
// console.log(intersectionWith(po.items?.map(item => item.name), query.items)) | // console.log(intersectionWith(po.items?.map(item => item.name), query.items)) | ||||
return po.code.toLowerCase().includes(query.code.toLowerCase()) | 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} | onReset={onReset} | ||||
/> | /> | ||||
<SearchResults<PickOrderResult> items={filteredPickOrders} columns={columns} pagingController={{ | |||||
pageNum: 0, | |||||
pageSize: 0, | |||||
totalCount: 0, | |||||
}} /> | |||||
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable"> | |||||
<Tab label={t("Pick Orders")} iconPosition="end" /> | |||||
<Tab label={t("Consolidated Pick Orders")} iconPosition="end" /> | |||||
</Tabs> | |||||
{tabIndex === 0 && <PickOrders filteredPickOrders={filteredPickOrders}/>} | |||||
</> | </> | ||||
) | ) | ||||
} | } | ||||
@@ -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<Props> = ({ | |||||
filteredPickOrders | |||||
}) => { | |||||
const { t } = useTranslation("pickOrder") | |||||
const [selectedRows, setSelectedRows] = useState<(string | number)[]>([]); | |||||
const handleConsolidatedRows = useCallback(() => { | |||||
}, [selectedRows]) | |||||
const columns = useMemo<Column<PickOrderResult>[]>(() => [ | |||||
{ | |||||
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 ( | |||||
<Grid container rowGap={1}> | |||||
<Grid item xs={3}> | |||||
<Button | |||||
disabled={selectedRows.length < 1} | |||||
variant="outlined" | |||||
> | |||||
{t("Consolidate")} | |||||
</Button> | |||||
</Grid> | |||||
<Grid item xs={12}> | |||||
<SearchResults<PickOrderResult> items={filteredPickOrders} columns={columns} pagingController={{ | |||||
pageNum: 0, | |||||
pageSize: 0 | |||||
}} | |||||
checkboxIds={selectedRows} | |||||
setCheckboxIds={setSelectedRows} | |||||
/> | |||||
</Grid> | |||||
</Grid> | |||||
) | |||||
} | |||||
export default PickOrders; |
@@ -1,6 +1,6 @@ | |||||
"use client"; | "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 Paper from "@mui/material/Paper"; | ||||
import Table from "@mui/material/Table"; | import Table from "@mui/material/Table"; | ||||
import TableBody from "@mui/material/TableBody"; | import TableBody from "@mui/material/TableBody"; | ||||
@@ -14,6 +14,7 @@ import TableRow from "@mui/material/TableRow"; | |||||
import IconButton, { IconButtonOwnProps } from "@mui/material/IconButton"; | import IconButton, { IconButtonOwnProps } from "@mui/material/IconButton"; | ||||
import { | import { | ||||
ButtonOwnProps, | ButtonOwnProps, | ||||
Checkbox, | |||||
Icon, | Icon, | ||||
IconOwnProps, | IconOwnProps, | ||||
SxProps, | SxProps, | ||||
@@ -26,7 +27,7 @@ export interface ResultWithId { | |||||
id: string | number; | id: string | number; | ||||
} | } | ||||
type ColumnType = "icon" | "decimal" | "integer"; | |||||
type ColumnType = "icon" | "decimal" | "integer" | "checkbox"; | |||||
interface BaseColumn<T extends ResultWithId> { | interface BaseColumn<T extends ResultWithId> { | ||||
name: keyof T; | name: keyof T; | ||||
@@ -56,6 +57,13 @@ interface IntegerColumn<T extends ResultWithId> extends BaseColumn<T> { | |||||
type: "integer"; | type: "integer"; | ||||
} | } | ||||
interface CheckboxColumn<T extends ResultWithId> extends BaseColumn<T> { | |||||
type: "checkbox"; | |||||
disabled?: (params: T) => boolean; | |||||
// checkboxIds: readonly (string | number)[], | |||||
// setCheckboxIds: (ids: readonly (string | number)[]) => void | |||||
} | |||||
interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> { | interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> { | ||||
onClick: (item: T) => void; | onClick: (item: T) => void; | ||||
buttonIcon: React.ReactNode; | buttonIcon: React.ReactNode; | ||||
@@ -67,6 +75,7 @@ export type Column<T extends ResultWithId> = | |||||
| BaseColumn<T> | | BaseColumn<T> | ||||
| IconColumn<T> | | IconColumn<T> | ||||
| DecimalColumn<T> | | DecimalColumn<T> | ||||
| CheckboxColumn<T> | |||||
| ColumnWithAction<T>; | | ColumnWithAction<T>; | ||||
interface Props<T extends ResultWithId> { | interface Props<T extends ResultWithId> { | ||||
@@ -77,9 +86,11 @@ interface Props<T extends ResultWithId> { | |||||
setPagingController?: Dispatch<SetStateAction<{ | setPagingController?: Dispatch<SetStateAction<{ | ||||
pageNum: number; | pageNum: number; | ||||
pageSize: number; | pageSize: number; | ||||
}>> | |||||
pagingController: { pageNum: number; pageSize: number;}; | |||||
}>> | |||||
pagingController: { pageNum: number; pageSize: number; }; | |||||
isAutoPaging?: boolean; | isAutoPaging?: boolean; | ||||
checkboxIds?: (string | number)[]; | |||||
setCheckboxIds?: Dispatch<SetStateAction<(string | number)[]>>; | |||||
} | } | ||||
function isActionColumn<T extends ResultWithId>( | function isActionColumn<T extends ResultWithId>( | ||||
@@ -106,14 +117,20 @@ function isIntegerColumn<T extends ResultWithId>( | |||||
return column.type === "integer"; | return column.type === "integer"; | ||||
} | } | ||||
function isCheckboxColumn<T extends ResultWithId>( | |||||
column: Column<T> | |||||
): column is CheckboxColumn<T> { | |||||
return column.type === "checkbox"; | |||||
} | |||||
// Icon Component Functions | // Icon Component Functions | ||||
function convertObjectKeysToLowercase<T extends object>( | function convertObjectKeysToLowercase<T extends object>( | ||||
obj: T | obj: T | ||||
): object | undefined { | ): object | undefined { | ||||
return obj | return obj | ||||
? Object.fromEntries( | ? Object.fromEntries( | ||||
Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value]) | |||||
) | |||||
Object.entries(obj).map(([key, value]) => [key.toLowerCase(), value]) | |||||
) | |||||
: undefined; | : undefined; | ||||
} | } | ||||
@@ -144,9 +161,9 @@ function handleIconIcons<T extends ResultWithId>( | |||||
return column.icon ?? <CheckCircleOutlineIcon fontSize="small" />; | return column.icon ?? <CheckCircleOutlineIcon fontSize="small" />; | ||||
} | } | ||||
export const defaultPagingController:{ pageNum: number; pageSize: number} = { | |||||
"pageNum": 1, | |||||
"pageSize": 10, | |||||
export const defaultPagingController: { pageNum: number; pageSize: number } = { | |||||
"pageNum": 1, | |||||
"pageSize": 10, | |||||
} | } | ||||
function SearchResults<T extends ResultWithId>({ | function SearchResults<T extends ResultWithId>({ | ||||
items, | items, | ||||
@@ -155,7 +172,9 @@ function SearchResults<T extends ResultWithId>({ | |||||
pagingController, | pagingController, | ||||
setPagingController, | setPagingController, | ||||
isAutoPaging = true, | isAutoPaging = true, | ||||
totalCount | |||||
totalCount, | |||||
checkboxIds = [], | |||||
setCheckboxIds = undefined, | |||||
}: Props<T>) { | }: Props<T>) { | ||||
const [page, setPage] = React.useState(0); | const [page, setPage] = React.useState(0); | ||||
const [rowsPerPage, setRowsPerPage] = React.useState(10); | const [rowsPerPage, setRowsPerPage] = React.useState(10); | ||||
@@ -189,6 +208,28 @@ function SearchResults<T extends ResultWithId>({ | |||||
} | } | ||||
}; | }; | ||||
// checkbox | |||||
const handleRowClick = useCallback((event: MouseEvent<unknown>, 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 = ( | const table = ( | ||||
<> | <> | ||||
<TableContainer sx={{ maxHeight: 440 }}> | <TableContainer sx={{ maxHeight: 440 }}> | ||||
@@ -209,29 +250,16 @@ function SearchResults<T extends ResultWithId>({ | |||||
<TableBody> | <TableBody> | ||||
{isAutoPaging | {isAutoPaging | ||||
? items | ? items | ||||
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) | |||||
.map((item) => { | |||||
return ( | |||||
<TableRow hover tabIndex={-1} key={item.id}> | |||||
{columns.map((column, idx) => { | |||||
const columnName = column.name; | |||||
return ( | |||||
<TabelCells | |||||
key={`${columnName.toString()}-${idx}`} | |||||
column={column} | |||||
columnName={columnName} | |||||
idx={idx} | |||||
item={item} | |||||
/> | |||||
); | |||||
})} | |||||
</TableRow> | |||||
); | |||||
}) | |||||
: items.map((item) => { | |||||
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) | |||||
.map((item) => { | |||||
return ( | return ( | ||||
<TableRow hover tabIndex={-1} key={item.id}> | |||||
<TableRow | |||||
hover | |||||
tabIndex={-1} | |||||
key={item.id} | |||||
onClick={setCheckboxIds ? (event) => handleRowClick(event, item.id) : undefined} | |||||
role={setCheckboxIds ? "checkbox" : undefined} | |||||
> | |||||
{columns.map((column, idx) => { | {columns.map((column, idx) => { | ||||
const columnName = column.name; | const columnName = column.name; | ||||
@@ -242,12 +270,33 @@ function SearchResults<T extends ResultWithId>({ | |||||
columnName={columnName} | columnName={columnName} | ||||
idx={idx} | idx={idx} | ||||
item={item} | item={item} | ||||
checkboxIds={checkboxIds} | |||||
/> | /> | ||||
); | ); | ||||
})} | })} | ||||
</TableRow> | </TableRow> | ||||
); | ); | ||||
})} | |||||
}) | |||||
: items.map((item) => { | |||||
return ( | |||||
<TableRow hover tabIndex={-1} key={item.id}> | |||||
{columns.map((column, idx) => { | |||||
const columnName = column.name; | |||||
return ( | |||||
<TabelCells | |||||
key={`${columnName.toString()}-${idx}`} | |||||
column={column} | |||||
columnName={columnName} | |||||
idx={idx} | |||||
item={item} | |||||
checkboxIds={checkboxIds} | |||||
/> | |||||
); | |||||
})} | |||||
</TableRow> | |||||
); | |||||
})} | |||||
</TableBody> | </TableBody> | ||||
</Table> | </Table> | ||||
</TableContainer> | </TableContainer> | ||||
@@ -255,8 +304,8 @@ function SearchResults<T extends ResultWithId>({ | |||||
rowsPerPageOptions={[10, 25, 100]} | rowsPerPageOptions={[10, 25, 100]} | ||||
component="div" | component="div" | ||||
count={!totalCount || totalCount == 0 | count={!totalCount || totalCount == 0 | ||||
? items.length | |||||
: totalCount | |||||
? items.length | |||||
: totalCount | |||||
} | } | ||||
// count={ | // count={ | ||||
// !pagingController || pagingController.totalCount == 0 | // !pagingController || pagingController.totalCount == 0 | ||||
@@ -280,6 +329,7 @@ interface TableCellsProps<T extends ResultWithId> { | |||||
columnName: keyof T; | columnName: keyof T; | ||||
idx: number; | idx: number; | ||||
item: T; | item: T; | ||||
checkboxIds: (string | number)[]; | |||||
} | } | ||||
function TabelCells<T extends ResultWithId>({ | function TabelCells<T extends ResultWithId>({ | ||||
@@ -287,7 +337,10 @@ function TabelCells<T extends ResultWithId>({ | |||||
columnName, | columnName, | ||||
idx, | idx, | ||||
item, | item, | ||||
checkboxIds = [], | |||||
}: TableCellsProps<T>) { | }: TableCellsProps<T>) { | ||||
const isItemSelected = checkboxIds.includes(item.id); | |||||
return ( | return ( | ||||
<TableCell | <TableCell | ||||
align={column.align} | align={column.align} | ||||
@@ -309,6 +362,8 @@ function TabelCells<T extends ResultWithId>({ | |||||
<>{decimalFormatter.format(Number(item[columnName]))}</> | <>{decimalFormatter.format(Number(item[columnName]))}</> | ||||
) : isIntegerColumn(column) ? ( | ) : isIntegerColumn(column) ? ( | ||||
<>{integerFormatter.format(Number(item[columnName]))}</> | <>{integerFormatter.format(Number(item[columnName]))}</> | ||||
) : isCheckboxColumn(column) ? ( | |||||
<Checkbox disabled={column.disabled ? column.disabled(item) : undefined} checked={isItemSelected} /> | |||||
) : column.renderCell ? ( | ) : column.renderCell ? ( | ||||
column.renderCell(item) | column.renderCell(item) | ||||
) : ( | ) : ( | ||||