@@ -1,6 +1,6 @@ | |||||
import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
import RoughSchedule from "@/components/RoughSchedule"; | import RoughSchedule from "@/components/RoughSchedule"; | ||||
import { getServerI18n } from "@/i18n"; | |||||
import { getServerI18n, I18nProvider } from "@/i18n"; | |||||
import Add from "@mui/icons-material/Add"; | import Add from "@mui/icons-material/Add"; | ||||
import Button from "@mui/material/Button"; | import Button from "@mui/material/Button"; | ||||
import Stack from "@mui/material/Stack"; | import Stack from "@mui/material/Stack"; | ||||
@@ -15,22 +15,22 @@ export const metadata: Metadata = { | |||||
}; | }; | ||||
const roughSchedulingDetail: React.FC = async () => { | const roughSchedulingDetail: React.FC = async () => { | ||||
const project = TypeEnum.PRODUCT | |||||
const { t } = await getServerI18n(project); | |||||
// const project = TypeEnum.PRODUCT | |||||
const { t } = await getServerI18n("project"); | |||||
// preloadClaims(); | // preloadClaims(); | ||||
return ( | return ( | ||||
<> | <> | ||||
<Stack | <Stack | ||||
direction="row" | direction="row" | ||||
justifyContent="space-between" | |||||
flexWrap="wrap" | |||||
rowGap={2} | |||||
> | |||||
<Typography variant="h4" marginInlineEnd={2}> | |||||
{t("Demand Forecast Detail")} | |||||
</Typography> | |||||
{/* <Button | |||||
justifyContent="space-between" | |||||
flexWrap="wrap" | |||||
rowGap={2} | |||||
> | |||||
<Typography variant="h4" marginInlineEnd={2}> | |||||
{t("Demand Forecast Detail")} | |||||
</Typography> | |||||
{/* <Button | |||||
variant="contained" | variant="contained" | ||||
startIcon={<Add />} | startIcon={<Add />} | ||||
LinkComponent={Link} | LinkComponent={Link} | ||||
@@ -38,12 +38,14 @@ const roughSchedulingDetail: React.FC = async () => { | |||||
> | > | ||||
{t("Create product")} | {t("Create product")} | ||||
</Button> */} | </Button> */} | ||||
</Stack> | |||||
<Suspense fallback={<RoughScheduleDetailView.Loading />}> | |||||
<RoughScheduleDetailView /> | |||||
</Suspense> | |||||
</> | |||||
); | |||||
</Stack> | |||||
<I18nProvider namespaces={["schedule"]}> | |||||
<Suspense fallback={<RoughScheduleDetailView.Loading />}> | |||||
<RoughScheduleDetailView /> | |||||
</Suspense> | |||||
</I18nProvider> | |||||
</> | |||||
); | |||||
}; | }; | ||||
export default roughSchedulingDetail; | export default roughSchedulingDetail; |
@@ -1,6 +1,6 @@ | |||||
import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
import RoughSchedule from "@/components/RoughSchedule"; | import RoughSchedule from "@/components/RoughSchedule"; | ||||
import { getServerI18n } from "@/i18n"; | |||||
import { getServerI18n, I18nProvider } from "@/i18n"; | |||||
import Add from "@mui/icons-material/Add"; | import Add from "@mui/icons-material/Add"; | ||||
import Button from "@mui/material/Button"; | import Button from "@mui/material/Button"; | ||||
import Stack from "@mui/material/Stack"; | import Stack from "@mui/material/Stack"; | ||||
@@ -14,8 +14,8 @@ export const metadata: Metadata = { | |||||
}; | }; | ||||
const roughScheduling: React.FC = async () => { | const roughScheduling: React.FC = async () => { | ||||
const project = TypeEnum.PRODUCT | |||||
const { t } = await getServerI18n(project); | |||||
// const project = TypeEnum.PRODUCT | |||||
const { t } = await getServerI18n("schedule"); | |||||
// preloadClaims(); | // preloadClaims(); | ||||
return ( | return ( | ||||
@@ -38,9 +38,11 @@ const roughScheduling: React.FC = async () => { | |||||
{t("Create product")} | {t("Create product")} | ||||
</Button> */} | </Button> */} | ||||
</Stack> | </Stack> | ||||
<Suspense fallback={<RoughSchedule.Loading />}> | |||||
<RoughSchedule /> | |||||
</Suspense> | |||||
<I18nProvider namespaces={["schedule"]}> | |||||
<Suspense fallback={<RoughSchedule.Loading />}> | |||||
<RoughSchedule /> | |||||
</Suspense> | |||||
</I18nProvider> | |||||
</> | </> | ||||
); | ); | ||||
}; | }; | ||||
@@ -8,6 +8,14 @@ export const moneyFormatter = new Intl.NumberFormat("en-HK", { | |||||
currency: "HKD", | currency: "HKD", | ||||
}); | }); | ||||
export const decimalFormatter = new Intl.NumberFormat("en-HK", { | |||||
minimumFractionDigits: 2, | |||||
}) | |||||
export const integerFormatter = new Intl.NumberFormat("en-HK", { | |||||
}) | |||||
export const stockInLineStatusMap: { [status: string]: {key: string, value: number} } = { | export const stockInLineStatusMap: { [status: string]: {key: string, value: number} } = { | ||||
draft: { key: "draft", value: 0 }, | draft: { key: "draft", value: 0 }, | ||||
pending: { key: "pending", value: 1 }, | pending: { key: "pending", value: 1 }, | ||||
@@ -20,7 +20,7 @@ interface Props { | |||||
type SearchQuery = Partial<Omit<QcItemResult, "id">>; | type SearchQuery = Partial<Omit<QcItemResult, "id">>; | ||||
type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
const qcItemSearch: React.FC<Props> = ({ qcItems }) => { | |||||
const QcItemSearch: React.FC<Props> = ({ qcItems }) => { | |||||
const { t } = useTranslation("qcItems"); | const { t } = useTranslation("qcItems"); | ||||
const router = useRouter(); | const router = useRouter(); | ||||
const pathname = usePathname() | const pathname = usePathname() | ||||
@@ -118,4 +118,4 @@ const qcItemSearch: React.FC<Props> = ({ qcItems }) => { | |||||
) | ) | ||||
}; | }; | ||||
export default qcItemSearch; | |||||
export default QcItemSearch; |
@@ -17,6 +17,7 @@ import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; | |||||
import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
import { CreateItemInputs } from "@/app/api/settings/item/actions"; | import { CreateItemInputs } from "@/app/api/settings/item/actions"; | ||||
import {NumberInputProps} from "@/components/CreateItem/NumberInputProps"; | import {NumberInputProps} from "@/components/CreateItem/NumberInputProps"; | ||||
import { integerFormatter } from "@/app/utils/formatUtil"; | |||||
type Props = { | type Props = { | ||||
// isEditMode: boolean; | // isEditMode: boolean; | ||||
@@ -68,7 +69,7 @@ const DetailInfoCard: React.FC<Props> = ({ recordDetails, isEditing}) => { | |||||
</Grid> | </Grid> | ||||
<Grid item xs={6}> | <Grid item xs={6}> | ||||
<TextField | <TextField | ||||
label={t("Total FG Type")} | |||||
label={t("Total FG Item")} | |||||
fullWidth | fullWidth | ||||
{...register("productCount", { | {...register("productCount", { | ||||
required: "code required!", | required: "code required!", | ||||
@@ -87,7 +88,7 @@ const DetailInfoCard: React.FC<Props> = ({ recordDetails, isEditing}) => { | |||||
required: "type required!", | required: "type required!", | ||||
})} | })} | ||||
disabled={!isEditing} | disabled={!isEditing} | ||||
defaultValue={details?.productionCount} | |||||
defaultValue={typeof(details?.productionCount) == "number" ? integerFormatter.format(details?.productionCount) : details?.productionCount} | |||||
error={Boolean(errors.type)} | error={Boolean(errors.type)} | ||||
helperText={errors.type?.message} | helperText={errors.type?.message} | ||||
/> | /> | ||||
@@ -43,7 +43,7 @@ const RoughScheduleDetailView: React.FC<Props> = ({ | |||||
console.log(params.get("id")) | console.log(params.get("id")) | ||||
const [serverError, setServerError] = useState(""); | const [serverError, setServerError] = useState(""); | ||||
const [tabIndex, setTabIndex] = useState(0); | const [tabIndex, setTabIndex] = useState(0); | ||||
const { t } = useTranslation(); | |||||
const { t } = useTranslation("schedule") | |||||
const router = useRouter(); | const router = useRouter(); | ||||
const [isEdit, setIsEdit] = useState(false); | const [isEdit, setIsEdit] = useState(false); | ||||
//const title = "Demand Forecast Detail" | //const title = "Demand Forecast Detail" | ||||
@@ -19,6 +19,8 @@ import { QcChecksInputs } from "@/app/api/settings/qcCheck/actions"; | |||||
import { GridApiCommunity } from "@mui/x-data-grid/internals"; | import { GridApiCommunity } from "@mui/x-data-grid/internals"; | ||||
import { RiceBowl } from "@mui/icons-material"; | import { RiceBowl } from "@mui/icons-material"; | ||||
import EditableSearchResults, {Column} from "@/components/SearchResults/EditableSearchResults"; | import EditableSearchResults, {Column} from "@/components/SearchResults/EditableSearchResults"; | ||||
import { decimalFormatter } from "@/app/utils/formatUtil"; | |||||
import { GridRenderCellParams } from "@mui/x-data-grid"; | |||||
type Props = { | type Props = { | ||||
apiRef: MutableRefObject<GridApiCommunity> | apiRef: MutableRefObject<GridApiCommunity> | ||||
@@ -57,7 +59,7 @@ const ViewByBomDetails: React.FC<Props> = ({ apiRef, isEdit }) => { | |||||
const { | const { | ||||
t, | t, | ||||
i18n: { language }, | i18n: { language }, | ||||
} = useTranslation(); | |||||
} = useTranslation("schedule"); | |||||
const { | const { | ||||
formState: { errors, defaultValues, touchedFields }, | formState: { errors, defaultValues, touchedFields }, | ||||
@@ -316,7 +318,7 @@ const ViewByBomDetails: React.FC<Props> = ({ apiRef, isEdit }) => { | |||||
() => [ | () => [ | ||||
{ | { | ||||
field: "name", | field: "name", | ||||
label: "name", | |||||
label: t("name"), | |||||
type: 'read-only', | type: 'read-only', | ||||
}, | }, | ||||
{ | { | ||||
@@ -329,46 +331,100 @@ const ViewByBomDetails: React.FC<Props> = ({ apiRef, isEdit }) => { | |||||
field: "inStockQty", | field: "inStockQty", | ||||
label: "In Stock Amount", | label: "In Stock Amount", | ||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.inStockQty) == "number") { | |||||
return decimalFormatter.format(row.inStockQty) | |||||
} | |||||
return row.inStockQty | |||||
} | |||||
// editable: true, | // editable: true, | ||||
}, | }, | ||||
{ | { | ||||
field: "overallPurchaseQty", | field: "overallPurchaseQty", | ||||
label: "Total Purchase Qty", | |||||
label: t("Total Demand Qty"), | |||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.overallPurchaseQty) == "number") { | |||||
return decimalFormatter.format(row.overallPurchaseQty) | |||||
} | |||||
return row.overallPurchaseQty | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
field: "purchaseQty1", | field: "purchaseQty1", | ||||
label: "Purchase Qty (Day1)", | |||||
label: t("Demand Qty (Day1)"), | |||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.purchaseQty1) == "number") { | |||||
return decimalFormatter.format(row.purchaseQty1) | |||||
} | |||||
return row.purchaseQty1 | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
field: "purchaseQty2", | field: "purchaseQty2", | ||||
label: "Purchase Qty (Day2)", | |||||
label: t("Demand Qty (Day2)"), | |||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.purchaseQty2) == "number") { | |||||
return decimalFormatter.format(row.purchaseQty2) | |||||
} | |||||
return row.purchaseQty2 | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
field: "purchaseQty3", | field: "purchaseQty3", | ||||
label: "Purchase Qty (Day3)", | |||||
label: t("Demand Qty (Day3)"), | |||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.purchaseQty3) == "number") { | |||||
return decimalFormatter.format(row.purchaseQty3) | |||||
} | |||||
return row.purchaseQty3 | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
field: "purchaseQty4", | field: "purchaseQty4", | ||||
label: "Purchase Qty (Day4)", | |||||
label: t("Demand Qty (Day4)"), | |||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.purchaseQty4) == "number") { | |||||
return decimalFormatter.format(row.purchaseQty4) | |||||
} | |||||
return row.purchaseQty4 | |||||
} | |||||
},{ | },{ | ||||
field: "purchaseQty5", | field: "purchaseQty5", | ||||
label: "Purchase Qty (Day5)", | |||||
label: t("Demand Qty (Day5)"), | |||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.purchaseQty5) == "number") { | |||||
return decimalFormatter.format(row.purchaseQty5) | |||||
} | |||||
return row.purchaseQty5 | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
field: "purchaseQty6", | field: "purchaseQty6", | ||||
label: "Purchase Qty (Day6)", | |||||
label: t("Demand Qty (Day6)"), | |||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.purchaseQty6) == "number") { | |||||
return decimalFormatter.format(row.purchaseQty6) | |||||
} | |||||
return row.purchaseQty6 | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
field: "purchaseQty7", | field: "purchaseQty7", | ||||
label: "Purchase Qty (Day7)", | |||||
label: t("Demand Qty (Day7)"), | |||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGOverallRecord) => { | |||||
if (typeof(row.purchaseQty7) == "number") { | |||||
return decimalFormatter.format(row.purchaseQty7) | |||||
} | |||||
return row.purchaseQty7 | |||||
} | |||||
}, | }, | ||||
], | ], | ||||
[] | [] | ||||
@@ -392,11 +448,23 @@ const ViewByBomDetails: React.FC<Props> = ({ apiRef, isEdit }) => { | |||||
label: "In Stock Amount", | label: "In Stock Amount", | ||||
type: 'read-only', | type: 'read-only', | ||||
// editable: true, | // editable: true, | ||||
renderCell: (row: FGRecord) => { | |||||
if (typeof(row.inStockQty) == "number") { | |||||
return decimalFormatter.format(row.inStockQty) | |||||
} | |||||
return row.inStockQty | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
field: "purchaseQty", | field: "purchaseQty", | ||||
label: "Purchase Qty", | label: "Purchase Qty", | ||||
type: 'read-only', | type: 'read-only', | ||||
renderCell: (row: FGRecord) => { | |||||
if (typeof(row.purchaseQty) == "number") { | |||||
return decimalFormatter.format(row.purchaseQty) | |||||
} | |||||
return row.purchaseQty | |||||
} | |||||
}, | }, | ||||
], | ], | ||||
[] | [] | ||||
@@ -19,6 +19,7 @@ import { QcChecksInputs } from "@/app/api/settings/qcCheck/actions"; | |||||
import { GridApiCommunity } from "@mui/x-data-grid/internals"; | import { GridApiCommunity } from "@mui/x-data-grid/internals"; | ||||
import { RiceBowl } from "@mui/icons-material"; | import { RiceBowl } from "@mui/icons-material"; | ||||
import EditableSearchResults, {Column} from "@/components/SearchResults/EditableSearchResults"; | import EditableSearchResults, {Column} from "@/components/SearchResults/EditableSearchResults"; | ||||
import { decimalFormatter } from "@/app/utils/formatUtil"; | |||||
type Props = { | type Props = { | ||||
apiRef: MutableRefObject<GridApiCommunity> | apiRef: MutableRefObject<GridApiCommunity> | ||||
@@ -460,11 +461,23 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit }) => { | |||||
label: "In Stock Amount", | label: "In Stock Amount", | ||||
type: 'read-only', | type: 'read-only', | ||||
// editable: true, | // editable: true, | ||||
renderCell: (row: FGRecord) => { | |||||
if (typeof(row.inStockQty) == "number") { | |||||
return decimalFormatter.format(row.inStockQty) | |||||
} | |||||
return row.inStockQty | |||||
} | |||||
}, | }, | ||||
{ | { | ||||
field: "productionQty", | field: "productionQty", | ||||
label: "Production Qty", | label: "Production Qty", | ||||
type: 'input', | type: 'input', | ||||
renderCell: (row: FGRecord) => { | |||||
if (typeof(row.productionQty) == "number") { | |||||
return decimalFormatter.format(row.productionQty ?? 0) | |||||
} | |||||
return row.productionQty | |||||
} | |||||
}, | }, | ||||
], | ], | ||||
[] | [] | ||||
@@ -487,24 +500,60 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit }) => { | |||||
field: "lastMonthAvgStock", | field: "lastMonthAvgStock", | ||||
label: "Last Month Average Stock", | label: "Last Month Average Stock", | ||||
type: 'read-only', | type: 'read-only', | ||||
style: { | |||||
textAlign: "center", | |||||
}, | |||||
renderCell: (row: FGRecord) => { | |||||
if (typeof(row.lastMonthAvgStock) == "number") { | |||||
return decimalFormatter.format(row.lastMonthAvgStock) | |||||
} | |||||
return row.lastMonthAvgStock | |||||
} | |||||
// editable: true, | // editable: true, | ||||
}, | }, | ||||
{ | { | ||||
field: "safetyStock", | field: "safetyStock", | ||||
label: "Safety Stock", | label: "Safety Stock", | ||||
type: 'read-only', | |||||
type: 'read-only-int', | |||||
style: { | |||||
textAlign: "center", | |||||
}, | |||||
renderCell: (row: FGRecord) => { | |||||
if (typeof(row.safetyStock) == "number") { | |||||
return decimalFormatter.format(row.safetyStock) | |||||
} | |||||
return row.safetyStock | |||||
} | |||||
// editable: true, | // editable: true, | ||||
}, | }, | ||||
{ | { | ||||
field: "inStockQty", | field: "inStockQty", | ||||
label: "In Stock Amount", | label: "In Stock Amount", | ||||
type: 'read-only', | |||||
type: 'read-only-int', | |||||
style: { | |||||
textAlign: "center", | |||||
}, | |||||
renderCell: (row: FGRecord) => { | |||||
if (typeof(row.inStockQty) == "number") { | |||||
return decimalFormatter.format(row.inStockQty) | |||||
} | |||||
return row.inStockQty | |||||
} | |||||
// editable: true, | // editable: true, | ||||
}, | }, | ||||
{ | { | ||||
field: "productionQty", | field: "productionQty", | ||||
label: "Production Qty", | label: "Production Qty", | ||||
type: 'input', | |||||
type: 'read-only-int', | |||||
style: { | |||||
textAlign: "right", | |||||
}, | |||||
renderCell: (row: FGRecord) => { | |||||
if (typeof(row.productionQty) == "number") { | |||||
return decimalFormatter.format(row.productionQty) | |||||
} | |||||
return row.productionQty | |||||
} | |||||
}, | }, | ||||
], | ], | ||||
[] | [] | ||||
@@ -1,6 +1,6 @@ | |||||
"use client"; | "use client"; | ||||
import React, {useEffect, useState} from "react"; | |||||
import React, { useEffect, 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"; | ||||
@@ -20,6 +20,7 @@ import { Collapse } from "@mui/material"; | |||||
import TempInputGridForMockUp from "./TempInputGridForMockUp"; | import TempInputGridForMockUp from "./TempInputGridForMockUp"; | ||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; | import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; | ||||
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; | import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; | ||||
import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; | |||||
export interface ResultWithId { | export interface ResultWithId { | ||||
id: string | number; | id: string | number; | ||||
@@ -31,6 +32,7 @@ interface BaseColumn<T extends ResultWithId> { | |||||
type: string; | type: string; | ||||
options?: T[]; | options?: T[]; | ||||
renderCell?: (T) => void; | renderCell?: (T) => void; | ||||
style?: Partial<HTMLElement["style"]> & { [propName: string]: string }; | |||||
} | } | ||||
interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> { | interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> { | ||||
@@ -47,31 +49,31 @@ interface Props<T extends ResultWithId> { | |||||
items: T[], | items: T[], | ||||
columns: Column<T>[], | columns: Column<T>[], | ||||
noWrapper?: boolean, | noWrapper?: boolean, | ||||
setPagingController: (value: { pageNum: number; pageSize: number; totalCount: number, index?: number}) => void, | |||||
setPagingController: (value: { pageNum: number; pageSize: number; totalCount: number, index?: number }) => void, | |||||
pagingController: { pageNum: number; pageSize: number; totalCount: number }, | pagingController: { pageNum: number; pageSize: number; totalCount: number }, | ||||
isAutoPaging: boolean | isAutoPaging: boolean | ||||
} | } | ||||
function EditableSearchResults<T extends ResultWithId>({ | function EditableSearchResults<T extends ResultWithId>({ | ||||
index, | |||||
items, | |||||
columns, | |||||
noWrapper, | |||||
pagingController, | |||||
setPagingController, | |||||
isAutoPaging = true, | |||||
isEdit = false, | |||||
isEditable = true, | |||||
hasCollapse = false, | |||||
}: Props<T>) { | |||||
index, | |||||
items, | |||||
columns, | |||||
noWrapper, | |||||
pagingController, | |||||
setPagingController, | |||||
isAutoPaging = true, | |||||
isEdit = false, | |||||
isEditable = true, | |||||
hasCollapse = false, | |||||
}: Props<T>) { | |||||
const [page, setPage] = useState(0); | const [page, setPage] = useState(0); | ||||
const [rowsPerPage, setRowsPerPage] = useState(10); | const [rowsPerPage, setRowsPerPage] = useState(10); | ||||
const [editingRowId, setEditingRowId] = useState<number | null>(null); | const [editingRowId, setEditingRowId] = useState<number | null>(null); | ||||
const [editedItems, setEditedItems] = useState<T[]>(items); | const [editedItems, setEditedItems] = useState<T[]>(items); | ||||
console.log(items) | |||||
useEffect(()=>{ | |||||
console.log(items) | |||||
useEffect(() => { | |||||
setEditedItems(items) | setEditedItems(items) | ||||
},[items]) | |||||
}, [items]) | |||||
const handleChangePage = (_event: unknown, newPage: number) => { | const handleChangePage = (_event: unknown, newPage: number) => { | ||||
setPage(newPage); | setPage(newPage); | ||||
setPagingController({ ...pagingController, pageNum: newPage + 1 }, (index ?? -1)); | setPagingController({ ...pagingController, pageNum: newPage + 1 }, (index ?? -1)); | ||||
@@ -80,7 +82,7 @@ function EditableSearchResults<T extends ResultWithId>({ | |||||
const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => { | const handleChangeRowsPerPage = (event: React.ChangeEvent<HTMLInputElement>) => { | ||||
setRowsPerPage(+event.target.value); | setRowsPerPage(+event.target.value); | ||||
setPage(0); | setPage(0); | ||||
setPagingController({ ...pagingController, pageSize: +event.target.value, pageNum: 1 , index: index}); | |||||
setPagingController({ ...pagingController, pageSize: +event.target.value, pageNum: 1, index: index }); | |||||
}; | }; | ||||
const handleEditClick = (id: number) => { | const handleEditClick = (id: number) => { | ||||
@@ -108,7 +110,7 @@ function EditableSearchResults<T extends ResultWithId>({ | |||||
setEditedItems((prev) => prev.filter(item => item.id !== id)); | setEditedItems((prev) => prev.filter(item => item.id !== id)); | ||||
}; | }; | ||||
useEffect(()=>{ | |||||
useEffect(() => { | |||||
console.log("[debug] isEdit in table", isEdit) | console.log("[debug] isEdit in table", isEdit) | ||||
//TODO: switch all record to not in edit mode and save the changes | //TODO: switch all record to not in edit mode and save the changes | ||||
if (!isEdit) { | if (!isEdit) { | ||||
@@ -120,7 +122,7 @@ function EditableSearchResults<T extends ResultWithId>({ | |||||
setEditingRowId(null); | setEditingRowId(null); | ||||
} | } | ||||
},[isEdit]) | |||||
}, [isEdit]) | |||||
function Row(props: { row: T }) { | function Row(props: { row: T }) { | ||||
const { row } = props; | const { row } = props; | ||||
@@ -128,128 +130,132 @@ function EditableSearchResults<T extends ResultWithId>({ | |||||
console.log(row) | console.log(row) | ||||
return ( | return ( | ||||
<> | <> | ||||
<TableRow hover tabIndex={-1} key={row.id}> | |||||
{ | |||||
(isEditable || hasCollapse) && <TableCell> | |||||
{(editingRowId === row.id) ? ( | |||||
<> | |||||
{ | |||||
isEditable && <IconButton disabled={!isEdit} onClick={() => handleSaveClick(row)}> | |||||
<SaveIcon/> | |||||
</IconButton> | |||||
} | |||||
{ | |||||
isEditable && <IconButton disabled={!isEdit} onClick={() => setEditingRowId(null)}> | |||||
<CancelIcon/> | |||||
</IconButton> | |||||
} | |||||
{ | |||||
hasCollapse && <IconButton | |||||
aria-label="expand row" | |||||
size="small" | |||||
onClick={() => setOpen(!open)} | |||||
> | |||||
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />} | |||||
</IconButton> | |||||
} | |||||
</> | |||||
) : ( | |||||
<> | |||||
{ | |||||
isEditable && <IconButton disabled={!isEdit} | |||||
onClick={() => handleEditClick(row.id as number)}> | |||||
<EditIcon/> | |||||
</IconButton> | |||||
} | |||||
{ | |||||
isEditable && <IconButton disabled={!isEdit} | |||||
onClick={() => handleDeleteClick(row.id as number)}> | |||||
<DeleteIcon/> | |||||
</IconButton> | |||||
} | |||||
{ | |||||
hasCollapse && <IconButton | |||||
aria-label="expand row" | |||||
size="small" | |||||
onClick={() => setOpen(!open)} | |||||
> | |||||
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />} | |||||
</IconButton> | |||||
} | |||||
</> | |||||
)} | |||||
</TableCell> | |||||
} | |||||
{columns.map((column, idx) => { | |||||
console.log(column) | |||||
const columnName = column.field; | |||||
return ( | |||||
<TableCell key={`${columnName.toString()}-${idx}`}> | |||||
{editingRowId === row.id ? ( | |||||
(() => { | |||||
switch (column.type) { | |||||
case 'input': | |||||
console.log(column.type) | |||||
return ( | |||||
<TextField | |||||
hiddenLabel={true} | |||||
fullWidth | |||||
defaultValue={row[columnName] as string} | |||||
onChange={(e) => handleInputChange(row.id as number, columnName, e.target.value)} | |||||
/> | |||||
); | |||||
case 'multi-select': | |||||
return ( | |||||
<MultiSelect | |||||
//label={column.label} | |||||
options={column.options} | |||||
selectedValues={[]} | |||||
onChange={(selectedValues) => handleInputChange(row.id as number, columnName, selectedValues)} | |||||
/> | |||||
); | |||||
case 'read-only': | |||||
return ( | |||||
<span> | |||||
{row[columnName] as string} | |||||
</span> | |||||
); | |||||
default: | |||||
return null; // Handle any default case if needed | |||||
<TableRow hover tabIndex={-1} key={row.id}> | |||||
{ | |||||
(isEditable || hasCollapse) && <TableCell> | |||||
{(editingRowId === row.id) ? ( | |||||
<> | |||||
{ | |||||
isEditable && <IconButton disabled={!isEdit} onClick={() => handleSaveClick(row)}> | |||||
<SaveIcon /> | |||||
</IconButton> | |||||
} | |||||
{ | |||||
isEditable && <IconButton disabled={!isEdit} onClick={() => setEditingRowId(null)}> | |||||
<CancelIcon /> | |||||
</IconButton> | |||||
} | } | ||||
})() | |||||
{ | |||||
hasCollapse && <IconButton | |||||
aria-label="expand row" | |||||
size="small" | |||||
onClick={() => setOpen(!open)} | |||||
> | |||||
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />} | |||||
</IconButton> | |||||
} | |||||
</> | |||||
) : ( | ) : ( | ||||
column.renderCell ? | |||||
column.renderCell(row) | |||||
: | |||||
<span onDoubleClick={() => isEdit && handleEditClick(row.id as number)}> | |||||
{row[columnName] as string} | |||||
</span> | |||||
<> | |||||
{ | |||||
isEditable && <IconButton disabled={!isEdit} | |||||
onClick={() => handleEditClick(row.id as number)}> | |||||
<EditIcon /> | |||||
</IconButton> | |||||
} | |||||
{ | |||||
isEditable && <IconButton disabled={!isEdit} | |||||
onClick={() => handleDeleteClick(row.id as number)}> | |||||
<DeleteIcon /> | |||||
</IconButton> | |||||
} | |||||
{ | |||||
hasCollapse && <IconButton | |||||
aria-label="expand row" | |||||
size="small" | |||||
onClick={() => setOpen(!open)} | |||||
> | |||||
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />} | |||||
</IconButton> | |||||
} | |||||
</> | |||||
)} | )} | ||||
</TableCell> | </TableCell> | ||||
); | |||||
})} | |||||
</TableRow> | |||||
<TableRow> | |||||
{ | |||||
hasCollapse && | |||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}> | |||||
<Collapse in={open} timeout="auto" unmountOnExit> | |||||
<Table> | |||||
<TableBody> | |||||
<TableRow> | |||||
<TableCell> | |||||
<TempInputGridForMockUp | |||||
stockInLine={row.lines as any[]} | |||||
/> | |||||
</TableCell> | |||||
</TableRow> | |||||
</TableBody> | |||||
</Table> | |||||
</Collapse> | |||||
</TableCell> | |||||
} | |||||
</TableRow> | |||||
</> | |||||
} | |||||
{columns.map((column, idx) => { | |||||
console.log(column) | |||||
const columnName = column.field; | |||||
return ( | |||||
<TableCell key={`${columnName.toString()}-${idx}`}> | |||||
{editingRowId === row.id ? ( | |||||
(() => { | |||||
switch (column.type) { | |||||
case 'input': | |||||
console.log(column.type) | |||||
return ( | |||||
<TextField | |||||
hiddenLabel={true} | |||||
fullWidth | |||||
defaultValue={row[columnName] as string} | |||||
onChange={(e) => handleInputChange(row.id as number, columnName, e.target.value)} | |||||
/> | |||||
); | |||||
case 'multi-select': | |||||
return ( | |||||
<MultiSelect | |||||
//label={column.label} | |||||
options={column.options} | |||||
selectedValues={[]} | |||||
onChange={(selectedValues) => handleInputChange(row.id as number, columnName, selectedValues)} | |||||
/> | |||||
); | |||||
case 'read-only': | |||||
return ( | |||||
<span> | |||||
{row[columnName] as string} | |||||
</span> | |||||
); | |||||
default: | |||||
return null; // Handle any default case if needed | |||||
} | |||||
})() | |||||
) : ( | |||||
column.renderCell ? | |||||
<div style={column.style}> | |||||
{column.renderCell(row)} | |||||
</div> | |||||
: | |||||
<div style={column.style}> | |||||
<span onDoubleClick={() => isEdit && handleEditClick(row.id as number)}> | |||||
{row[columnName] as String} | |||||
</span> | |||||
</div> | |||||
)} | |||||
</TableCell> | |||||
); | |||||
})} | |||||
</TableRow> | |||||
<TableRow> | |||||
{ | |||||
hasCollapse && | |||||
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}> | |||||
<Collapse in={open} timeout="auto" unmountOnExit> | |||||
<Table> | |||||
<TableBody> | |||||
<TableRow> | |||||
<TableCell> | |||||
<TempInputGridForMockUp | |||||
stockInLine={row.lines as any[]} | |||||
/> | |||||
</TableCell> | |||||
</TableRow> | |||||
</TableBody> | |||||
</Table> | |||||
</Collapse> | |||||
</TableCell> | |||||
} | |||||
</TableRow> | |||||
</> | |||||
) | ) | ||||
} | } | ||||
@@ -261,7 +267,7 @@ function EditableSearchResults<T extends ResultWithId>({ | |||||
<TableRow> | <TableRow> | ||||
{(isEditable || hasCollapse) && <TableCell>Actions</TableCell>} {/* Action Column Header */} | {(isEditable || hasCollapse) && <TableCell>Actions</TableCell>} {/* Action Column Header */} | ||||
{columns.map((column, idx) => ( | {columns.map((column, idx) => ( | ||||
<TableCell key={`${column.field.toString()}${idx}`}> | |||||
<TableCell style={column.style} key={`${column.field.toString()}${idx}`}> | |||||
{column.label} | {column.label} | ||||
</TableCell> | </TableCell> | ||||
))} | ))} | ||||
@@ -35,7 +35,7 @@ import { QcItemWithChecks } from "src/app/api/qc"; | |||||
import PlayArrowIcon from "@mui/icons-material/PlayArrow"; | import PlayArrowIcon from "@mui/icons-material/PlayArrow"; | ||||
import { createStockInLine, testFetch } from "@/app/api/po/actions"; | import { createStockInLine, testFetch } from "@/app/api/po/actions"; | ||||
import { useSearchParams } from "next/navigation"; | import { useSearchParams } from "next/navigation"; | ||||
import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | |||||
import { decimalFormatter, stockInLineStatusMap } from "@/app/utils/formatUtil"; | |||||
interface ResultWithId { | interface ResultWithId { | ||||
id: number; | id: number; | ||||
@@ -180,13 +180,19 @@ function TempInputGridForMockUp({ stockInLine }: Props) { | |||||
flex: 0.5, | flex: 0.5, | ||||
type: "number", | type: "number", | ||||
editable: true, | editable: true, | ||||
renderCell: (row) => { | |||||
return decimalFormatter.format(row.value) | |||||
} | |||||
// replace with tooltip + content | // replace with tooltip + content | ||||
}, | }, | ||||
{ | { | ||||
field: "purchaseQty", | field: "purchaseQty", | ||||
headerName: "purchaseQty", | headerName: "purchaseQty", | ||||
flex: 0.5, | flex: 0.5, | ||||
editable: true | |||||
editable: true, | |||||
renderCell: (row) => { | |||||
return decimalFormatter.format(row.value) | |||||
} | |||||
}, | }, | ||||
// { | // { | ||||
// field: "actions", | // field: "actions", | ||||
@@ -0,0 +1,10 @@ | |||||
{ | |||||
"Total Demand Qty": "Total Demand Qty", | |||||
"Demand Qty (Day1)": "Demand Qty (Day1)", | |||||
"Demand Qty (Day2)": "Demand Qty (Day2)", | |||||
"Demand Qty (Day3)": "Demand Qty (Day3)", | |||||
"Demand Qty (Day4)": "Demand Qty (Day4)", | |||||
"Demand Qty (Day5)": "Demand Qty (Day5)", | |||||
"Demand Qty (Day6)": "Demand Qty (Day6)", | |||||
"Demand Qty (Day7)": "Demand Qty (Day7)" | |||||
} |
@@ -0,0 +1,10 @@ | |||||
{ | |||||
"Total Demand Qty": "Total Demand Qty", | |||||
"Demand Qty (Day1)": "Demand Qty (Day1)", | |||||
"Demand Qty (Day2)": "Demand Qty (Day2)", | |||||
"Demand Qty (Day3)": "Demand Qty (Day3)", | |||||
"Demand Qty (Day4)": "Demand Qty (Day4)", | |||||
"Demand Qty (Day5)": "Demand Qty (Day5)", | |||||
"Demand Qty (Day6)": "Demand Qty (Day6)", | |||||
"Demand Qty (Day7)": "Demand Qty (Day7)" | |||||
} |