| @@ -0,0 +1,110 @@ | |||||
| import { InventoryLotLineResult, InventoryResult } from "@/app/api/inventory"; | |||||
| import { Dispatch, SetStateAction, useMemo } from "react"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { Column } from "../SearchResults"; | |||||
| import SearchResults, { defaultPagingController, defaultSetPagingController } from "../SearchResults/SearchResults"; | |||||
| import { CheckCircleOutline, DoDisturb } from "@mui/icons-material"; | |||||
| import { arrayToDateString } from "@/app/utils/formatUtil"; | |||||
| import { Typography } from "@mui/material"; | |||||
| import { isFinite } from "lodash"; | |||||
| interface Props { | |||||
| inventoryLotLines: InventoryLotLineResult[] | null; | |||||
| setPagingController: defaultSetPagingController; | |||||
| pagingController: typeof defaultPagingController; | |||||
| totalCount: number; | |||||
| item: InventoryResult | null; | |||||
| } | |||||
| const InventoryLotLineTable: React.FC<Props> = ({ inventoryLotLines, pagingController, setPagingController, totalCount, item }) => { | |||||
| const { t } = useTranslation(["inventory"]); | |||||
| const columns = useMemo<Column<InventoryLotLineResult>[]>( | |||||
| () => [ | |||||
| // { | |||||
| // name: "item", | |||||
| // label: t("Code"), | |||||
| // renderCell: (params) => { | |||||
| // return params.item.code; | |||||
| // }, | |||||
| // }, | |||||
| // { | |||||
| // name: "item", | |||||
| // label: t("Name"), | |||||
| // renderCell: (params) => { | |||||
| // return params.item.name; | |||||
| // }, | |||||
| // }, | |||||
| { | |||||
| name: "lotNo", | |||||
| label: t("Lot No"), | |||||
| }, | |||||
| // { | |||||
| // name: "item", | |||||
| // label: t("Type"), | |||||
| // renderCell: (params) => { | |||||
| // return t(params.item.type); | |||||
| // }, | |||||
| // }, | |||||
| { | |||||
| name: "availableQty", | |||||
| label: t("Available Qty"), | |||||
| align: "right", | |||||
| headerAlign: "right", | |||||
| type: "integer", | |||||
| }, | |||||
| { | |||||
| name: "uom", | |||||
| label: t("Sales UoM"), | |||||
| align: "left", | |||||
| headerAlign: "left", | |||||
| }, | |||||
| { | |||||
| name: "qtyPerSmallestUnit", | |||||
| label: t("Available Qty Per Smallest Unit"), | |||||
| align: "right", | |||||
| headerAlign: "right", | |||||
| type: "integer", | |||||
| }, | |||||
| { | |||||
| name: "baseUom", | |||||
| label: t("Base UoM"), | |||||
| align: "left", | |||||
| headerAlign: "left", | |||||
| }, | |||||
| { | |||||
| name: "expiryDate", | |||||
| label: t("Expiry Date"), | |||||
| renderCell: (params) => { | |||||
| return arrayToDateString(params.expiryDate) | |||||
| }, | |||||
| }, | |||||
| { | |||||
| name: "status", | |||||
| label: t("Status"), | |||||
| type: "icon", | |||||
| icons: { | |||||
| available: <CheckCircleOutline fontSize="small"/>, | |||||
| unavailable: <DoDisturb fontSize="small"/>, | |||||
| }, | |||||
| colors: { | |||||
| available: "success", | |||||
| unavailable: "error", | |||||
| } | |||||
| }, | |||||
| ], | |||||
| [t], | |||||
| ); | |||||
| return <> | |||||
| <Typography variant="h6">{item ? `${t("Item selected")}: ${item.itemCode} | ${item.itemName} (${t(item.itemType)})` : t("No items are selected yet.")}</Typography> | |||||
| <SearchResults<InventoryLotLineResult> | |||||
| items={inventoryLotLines ?? []} | |||||
| columns={columns} | |||||
| pagingController={pagingController} | |||||
| setPagingController={setPagingController} | |||||
| totalCount={totalCount} | |||||
| /> | |||||
| </> | |||||
| } | |||||
| export default InventoryLotLineTable; | |||||
| @@ -1,11 +1,15 @@ | |||||
| "use client"; | "use client"; | ||||
| import { InventoryResult } from "@/app/api/inventory"; | |||||
| import { InventoryLotLineResult, InventoryResult } from "@/app/api/inventory"; | |||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import SearchBox, { Criterion } from "../SearchBox"; | import SearchBox, { Criterion } from "../SearchBox"; | ||||
| import { useCallback, useMemo, useState } from "react"; | |||||
| import { uniq } from "lodash"; | |||||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import { orderBy, uniq, uniqBy } from "lodash"; | |||||
| import SearchResults, { Column } from "../SearchResults"; | import SearchResults, { Column } from "../SearchResults"; | ||||
| import { CheckCircleOutline, DoDisturb } from "@mui/icons-material"; | import { CheckCircleOutline, DoDisturb } from "@mui/icons-material"; | ||||
| import InventoryTable from "./InventoryTable"; | |||||
| import { defaultPagingController } from "../SearchResults/SearchResults"; | |||||
| import InventoryLotLineTable from "./InventoryLotLineTable"; | |||||
| import { SearchInventory, SearchInventoryLotLine, fetchInventories, fetchInventoryLotLines } from "@/app/api/inventory/actions"; | |||||
| interface Props { | interface Props { | ||||
| inventories: InventoryResult[]; | inventories: InventoryResult[]; | ||||
| @@ -31,7 +35,30 @@ type SearchParamNames = keyof SearchQuery; | |||||
| const InventorySearch: React.FC<Props> = ({ inventories }) => { | const InventorySearch: React.FC<Props> = ({ inventories }) => { | ||||
| const { t } = useTranslation(["inventory", "common"]); | const { t } = useTranslation(["inventory", "common"]); | ||||
| const [filteredInventories, setFilteredInventories] = useState(inventories); | |||||
| // Inventory | |||||
| const [filteredInventories, setFilteredInventories] = useState<InventoryResult[]>([]); | |||||
| const [inventoriesPagingController, setInventoriesPagingController] = useState(defaultPagingController) | |||||
| const [inventoriesTotalCount, setInventoriesTotalCount] = useState(0) | |||||
| const [item, setItem] = useState<InventoryResult | null>(null) | |||||
| // Inventory Lot Line | |||||
| const [filteredInventoryLotLines, setFilteredInventoryLotLines] = useState<InventoryLotLineResult[]>([]); | |||||
| const [inventoryLotLinesPagingController, setInventoryLotLinesPagingController] = useState(defaultPagingController) | |||||
| const [inventoryLotLinesTotalCount, setInventoryLotLinesTotalCount] = useState(0) | |||||
| const [inputs, setInputs] = useState<Record<SearchParamNames, string>>({ | |||||
| itemId: "", | |||||
| itemCode: "", | |||||
| itemName: "", | |||||
| itemType: "", | |||||
| onHandQty: "", | |||||
| onHoldQty: "", | |||||
| unavailableQty: "", | |||||
| availableQty: "", | |||||
| currencyName: "", | |||||
| status: "", | |||||
| baseUom: "", | |||||
| }); | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | ||||
| () => [ | () => [ | ||||
| @@ -43,88 +70,102 @@ const InventorySearch: React.FC<Props> = ({ inventories }) => { | |||||
| type: "select", | type: "select", | ||||
| options: uniq(inventories.map((i) => i.itemType)), | options: uniq(inventories.map((i) => i.itemType)), | ||||
| }, | }, | ||||
| { | |||||
| label: t("Status"), | |||||
| paramName: "status", | |||||
| type: "select", | |||||
| options: uniq(inventories.map((i) => i.status)), | |||||
| }, | |||||
| // { | |||||
| // label: t("Status"), | |||||
| // paramName: "status", | |||||
| // type: "select", | |||||
| // options: uniq(inventories.map((i) => i.status)), | |||||
| // }, | |||||
| ], | ], | ||||
| [t], | [t], | ||||
| ); | ); | ||||
| const refetchInventoryData = useCallback(async ( | |||||
| query: Record<SearchParamNames, string>, | |||||
| actionType: "reset" | "search" | "paging" | |||||
| ) => { | |||||
| const params: SearchInventory = { | |||||
| code: query?.itemCode ?? '', | |||||
| name: query?.itemName ?? '', | |||||
| type: query?.itemType.toLowerCase() === "all" ? '' : query?.itemType ?? '', | |||||
| pageNum: inventoriesPagingController.pageNum - 1, | |||||
| pageSize: inventoriesPagingController.pageSize | |||||
| } | |||||
| const response = await fetchInventories(params) | |||||
| if (response) { | |||||
| console.log(response) | |||||
| setInventoriesTotalCount(response.total); | |||||
| switch (actionType) { | |||||
| case "reset": | |||||
| case "search": | |||||
| setFilteredInventories(() => response.records); | |||||
| break; | |||||
| case "paging": | |||||
| setFilteredInventories((fi) => | |||||
| orderBy( | |||||
| uniqBy([...fi, ...response.records], "id"), | |||||
| ["id"], ["desc"] | |||||
| )); | |||||
| } | |||||
| } | |||||
| }, [inventoriesPagingController, setInventoriesPagingController]) | |||||
| useEffect(() => { | |||||
| refetchInventoryData(inputs, "paging") | |||||
| }, [inventoriesPagingController]) | |||||
| const refetchInventoryLotLineData = useCallback(async ( | |||||
| itemId: number | null, | |||||
| actionType: "reset" | "search" | "paging" | |||||
| ) => { | |||||
| if (!itemId) { | |||||
| setItem(null) | |||||
| setInventoryLotLinesTotalCount(0); | |||||
| setFilteredInventoryLotLines([]) | |||||
| return | |||||
| } | |||||
| const params: SearchInventoryLotLine = { | |||||
| itemId: itemId, | |||||
| pageNum: inventoriesPagingController.pageNum - 1, | |||||
| pageSize: inventoriesPagingController.pageSize | |||||
| } | |||||
| const response = await fetchInventoryLotLines(params) | |||||
| if (response) { | |||||
| setInventoryLotLinesTotalCount(response.total); | |||||
| switch (actionType) { | |||||
| case "reset": | |||||
| case "search": | |||||
| setFilteredInventoryLotLines(() => response.records); | |||||
| break; | |||||
| case "paging": | |||||
| setFilteredInventoryLotLines((fi) => | |||||
| orderBy( | |||||
| uniqBy([...fi, ...response.records], "id"), | |||||
| ["id"], ["desc"] | |||||
| )); | |||||
| } | |||||
| } | |||||
| }, [inventoriesPagingController, setInventoriesPagingController]) | |||||
| useEffect(() => { | |||||
| refetchInventoryData(inputs, "paging") | |||||
| }, [inventoriesPagingController]) | |||||
| const onReset = useCallback(() => { | const onReset = useCallback(() => { | ||||
| setFilteredInventories(inventories); | |||||
| }, [inventories]); | |||||
| refetchInventoryData(inputs, "reset"); | |||||
| refetchInventoryLotLineData(null, "reset"); | |||||
| // setFilteredInventories(inventories); | |||||
| }, []); | |||||
| const columns = useMemo<Column<InventoryResult>[]>( | |||||
| () => [ | |||||
| { | |||||
| name: "itemCode", | |||||
| label: t("Code"), | |||||
| }, | |||||
| { | |||||
| name: "itemName", | |||||
| label: t("Name"), | |||||
| }, | |||||
| { | |||||
| name: "itemType", | |||||
| label: t("Type"), | |||||
| renderCell: (params) => { | |||||
| return t(params.itemType); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| name: "availableQty", | |||||
| label: t("Qty"), | |||||
| align: "right", | |||||
| headerAlign: "right", | |||||
| type: "integer", | |||||
| }, | |||||
| { | |||||
| name: "uomUdfudesc", | |||||
| label: t("UoM"), | |||||
| }, | |||||
| // { | |||||
| // name: "qtyPerSmallestUnit", | |||||
| // label: t("Qty Per Smallest Unit"), | |||||
| // align: "right", | |||||
| // headerAlign: "right", | |||||
| // type: "decimal" | |||||
| // }, | |||||
| // { | |||||
| // name: "smallestUnit", | |||||
| // label: t("Smallest Unit"), | |||||
| // }, | |||||
| // { | |||||
| // name: "price", | |||||
| // label: t("Price"), | |||||
| // align: "right", | |||||
| // sx: { | |||||
| // alignItems: "right", | |||||
| // justifyContent: "end", | |||||
| // } | |||||
| // }, | |||||
| // { | |||||
| // name: "currencyName", | |||||
| // label: t("Currency"), | |||||
| // }, | |||||
| // { | |||||
| // name: "status", | |||||
| // label: t("Status"), | |||||
| // type: "icon", | |||||
| // icons: { | |||||
| // available: <CheckCircleOutline fontSize="small"/>, | |||||
| // unavailable: <DoDisturb fontSize="small"/>, | |||||
| // }, | |||||
| // colors: { | |||||
| // available: "success", | |||||
| // unavailable: "error", | |||||
| // } | |||||
| // }, | |||||
| ], | |||||
| [t], | |||||
| ); | |||||
| const onInventoryRowClick = useCallback((item: InventoryResult) => { | |||||
| setItem(item) | |||||
| refetchInventoryLotLineData(item.itemId, "search") | |||||
| }, []) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| @@ -133,28 +174,35 @@ const InventorySearch: React.FC<Props> = ({ inventories }) => { | |||||
| onSearch={(query) => { | onSearch={(query) => { | ||||
| // console.log(query) | // console.log(query) | ||||
| // console.log(inventories) | // console.log(inventories) | ||||
| setFilteredInventories( | |||||
| inventories.filter( | |||||
| (i) => | |||||
| i.itemCode.toLowerCase().includes(query.itemCode.toLowerCase()) && | |||||
| i.itemName.toLowerCase().includes(query.itemName.toLowerCase()) && | |||||
| (query.itemType == "All" || | |||||
| i.itemType.toLowerCase().includes(query.itemType.toLowerCase())) && | |||||
| (query.status == "All" || | |||||
| i.status.toLowerCase().includes(query.status.toLowerCase())), | |||||
| ), | |||||
| ); | |||||
| setInputs(() => query) | |||||
| refetchInventoryData(query, "search") | |||||
| // setFilteredInventories( | |||||
| // inventories.filter( | |||||
| // (i) => | |||||
| // i.itemCode.toLowerCase().includes(query.itemCode.toLowerCase()) && | |||||
| // i.itemName.toLowerCase().includes(query.itemName.toLowerCase()) && | |||||
| // (query.itemType == "All" || | |||||
| // i.itemType.toLowerCase().includes(query.itemType.toLowerCase())) && | |||||
| // (query.status == "All" || | |||||
| // i.status.toLowerCase().includes(query.status.toLowerCase())), | |||||
| // ), | |||||
| // ); | |||||
| }} | }} | ||||
| onReset={onReset} | onReset={onReset} | ||||
| /> | /> | ||||
| <SearchResults<InventoryResult> | |||||
| items={filteredInventories} | |||||
| columns={columns} | |||||
| pagingController={{ | |||||
| pageNum: 0, | |||||
| pageSize: 0, | |||||
| // totalCount: 0, | |||||
| }} | |||||
| <InventoryTable | |||||
| inventories={filteredInventories} | |||||
| pagingController={inventoriesPagingController} | |||||
| setPagingController={setInventoriesPagingController} | |||||
| totalCount={inventoriesTotalCount} | |||||
| onRowClick={onInventoryRowClick} | |||||
| /> | |||||
| <InventoryLotLineTable | |||||
| inventoryLotLines={filteredInventoryLotLines} | |||||
| pagingController={inventoryLotLinesPagingController} | |||||
| setPagingController={setInventoryLotLinesPagingController} | |||||
| totalCount={inventoryLotLinesTotalCount} | |||||
| item={item} | |||||
| /> | /> | ||||
| </> | </> | ||||
| ); | ); | ||||
| @@ -0,0 +1,111 @@ | |||||
| import { InventoryResult } from "@/app/api/inventory"; | |||||
| import { Dispatch, SetStateAction, useMemo } from "react"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { Column } from "../SearchResults"; | |||||
| import SearchResults, { defaultPagingController, defaultSetPagingController } from "../SearchResults/SearchResults"; | |||||
| interface Props { | |||||
| inventories: InventoryResult[]; | |||||
| setPagingController: defaultSetPagingController; | |||||
| pagingController: typeof defaultPagingController; | |||||
| totalCount: number; | |||||
| onRowClick: (item: InventoryResult) => void; | |||||
| } | |||||
| const InventoryTable: React.FC<Props> = ({ inventories, pagingController, setPagingController, totalCount, onRowClick }) => { | |||||
| const { t } = useTranslation(["inventory"]); | |||||
| const columns = useMemo<Column<InventoryResult>[]>( | |||||
| () => [ | |||||
| { | |||||
| name: "itemCode", | |||||
| label: t("Code"), | |||||
| }, | |||||
| { | |||||
| name: "itemName", | |||||
| label: t("Name"), | |||||
| }, | |||||
| { | |||||
| name: "itemType", | |||||
| label: t("Type"), | |||||
| renderCell: (params) => { | |||||
| return t(params.itemType); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| name: "availableQty", | |||||
| label: t("Available Qty"), | |||||
| align: "right", | |||||
| headerAlign: "right", | |||||
| type: "integer", | |||||
| }, | |||||
| { | |||||
| name: "uomUdfudesc", | |||||
| label: t("Sales UoM"), | |||||
| align: "left", | |||||
| headerAlign: "left", | |||||
| }, | |||||
| { | |||||
| name: "qtyPerSmallestUnit", | |||||
| label: t("Available Qty Per Smallest Unit"), | |||||
| align: "right", | |||||
| headerAlign: "right", | |||||
| type: "integer", | |||||
| }, | |||||
| { | |||||
| name: "baseUom", | |||||
| label: t("Base UoM"), | |||||
| align: "left", | |||||
| headerAlign: "left", | |||||
| }, | |||||
| // { | |||||
| // name: "qtyPerSmallestUnit", | |||||
| // label: t("Qty Per Smallest Unit"), | |||||
| // align: "right", | |||||
| // headerAlign: "right", | |||||
| // type: "decimal" | |||||
| // }, | |||||
| // { | |||||
| // name: "smallestUnit", | |||||
| // label: t("Smallest Unit"), | |||||
| // }, | |||||
| // { | |||||
| // name: "price", | |||||
| // label: t("Price"), | |||||
| // align: "right", | |||||
| // sx: { | |||||
| // alignItems: "right", | |||||
| // justifyContent: "end", | |||||
| // } | |||||
| // }, | |||||
| // { | |||||
| // name: "currencyName", | |||||
| // label: t("Currency"), | |||||
| // }, | |||||
| // { | |||||
| // name: "status", | |||||
| // label: t("Status"), | |||||
| // type: "icon", | |||||
| // icons: { | |||||
| // available: <CheckCircleOutline fontSize="small"/>, | |||||
| // unavailable: <DoDisturb fontSize="small"/>, | |||||
| // }, | |||||
| // colors: { | |||||
| // available: "success", | |||||
| // unavailable: "error", | |||||
| // } | |||||
| // }, | |||||
| ], | |||||
| [t], | |||||
| ); | |||||
| return <SearchResults<InventoryResult> | |||||
| items={inventories} | |||||
| columns={columns} | |||||
| pagingController={pagingController} | |||||
| setPagingController={setPagingController} | |||||
| totalCount={totalCount} | |||||
| onRowClick={onRowClick} | |||||
| /> | |||||
| } | |||||
| export default InventoryTable; | |||||
| @@ -248,7 +248,7 @@ function SearchBox<T extends string>({ | |||||
| <MenuItem value={"All"}>{t("All")}</MenuItem> | <MenuItem value={"All"}>{t("All")}</MenuItem> | ||||
| {c.options.map((option) => ( | {c.options.map((option) => ( | ||||
| <MenuItem key={option} value={option}> | <MenuItem key={option} value={option}> | ||||
| {option} | |||||
| {t(option)} | |||||
| </MenuItem> | </MenuItem> | ||||
| ))} | ))} | ||||
| </Select> | </Select> | ||||
| @@ -100,6 +100,7 @@ interface Props<T extends ResultWithId> { | |||||
| isAutoPaging?: boolean; | isAutoPaging?: boolean; | ||||
| checkboxIds?: (string | number)[]; | checkboxIds?: (string | number)[]; | ||||
| setCheckboxIds?: Dispatch<SetStateAction<(string | number)[]>>; | setCheckboxIds?: Dispatch<SetStateAction<(string | number)[]>>; | ||||
| onRowClick?: (item: T) => void; | |||||
| } | } | ||||
| function isActionColumn<T extends ResultWithId>( | function isActionColumn<T extends ResultWithId>( | ||||
| @@ -138,8 +139,8 @@ function convertObjectKeysToLowercase<T extends object>( | |||||
| ): 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; | ||||
| } | } | ||||
| @@ -174,6 +175,14 @@ export const defaultPagingController: { pageNum: number; pageSize: number } = { | |||||
| pageNum: 1, | pageNum: 1, | ||||
| pageSize: 10, | pageSize: 10, | ||||
| }; | }; | ||||
| export type defaultSetPagingController = Dispatch< | |||||
| SetStateAction<{ | |||||
| pageNum: number; | |||||
| pageSize: number; | |||||
| }> | |||||
| > | |||||
| function SearchResults<T extends ResultWithId>({ | function SearchResults<T extends ResultWithId>({ | ||||
| items, | items, | ||||
| columns, | columns, | ||||
| @@ -184,6 +193,7 @@ function SearchResults<T extends ResultWithId>({ | |||||
| totalCount, | totalCount, | ||||
| checkboxIds = [], | checkboxIds = [], | ||||
| setCheckboxIds = undefined, | setCheckboxIds = undefined, | ||||
| onRowClick = 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); | ||||
| @@ -279,40 +289,25 @@ 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} | |||||
| onClick={ | |||||
| .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) | |||||
| .map((item) => { | |||||
| return ( | |||||
| <TableRow | |||||
| hover | |||||
| tabIndex={-1} | |||||
| key={item.id} | |||||
| onClick={(event) => { | |||||
| setCheckboxIds | setCheckboxIds | ||||
| ? (event) => handleRowClick(event, item, columns) | |||||
| ? handleRowClick(event, item, columns) | |||||
| : undefined | : undefined | ||||
| if (onRowClick) { | |||||
| onRowClick(item) | |||||
| } | |||||
| } | } | ||||
| role={setCheckboxIds ? "checkbox" : undefined} | |||||
| > | |||||
| {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> | |||||
| ); | |||||
| }) | |||||
| : items.map((item) => { | |||||
| return ( | |||||
| <TableRow hover tabIndex={-1} key={item.id}> | |||||
| } | |||||
| role={setCheckboxIds ? "checkbox" : undefined} | |||||
| > | |||||
| {columns.map((column, idx) => { | {columns.map((column, idx) => { | ||||
| const columnName = column.name; | const columnName = column.name; | ||||
| @@ -329,7 +324,27 @@ function SearchResults<T extends ResultWithId>({ | |||||
| })} | })} | ||||
| </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> | ||||
| @@ -35,6 +35,7 @@ | |||||
| "Project":"專案", | "Project":"專案", | ||||
| "Product":"產品", | "Product":"產品", | ||||
| "Material":"材料", | "Material":"材料", | ||||
| "mat":"原料", | |||||
| "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":"查看物料出入庫及庫存日誌", | ||||
| @@ -7,5 +7,13 @@ | |||||
| "Qty": "數量", | "Qty": "數量", | ||||
| "UoM": "單位", | "UoM": "單位", | ||||
| "mat": "物料", | "mat": "物料", | ||||
| "fg": "成品" | |||||
| } | |||||
| "fg": "成品", | |||||
| "Available Qty": "可用數量 (銷售單位)", | |||||
| "Sales UoM": "銷售單位", | |||||
| "Available Qty Per Smallest Unit": "可用數量 (基本單位)", | |||||
| "Base UoM": "基本單位", | |||||
| "Lot No": "批號", | |||||
| "Expiry Date": "到期日", | |||||
| "No items are selected yet.": "未選擇項目", | |||||
| "Item selected": "已選擇項目" | |||||
| } | |||||