| @@ -4,7 +4,8 @@ import { convertObjToURLSearchParams } from "@/app/utils/commonUtil"; | |||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
| import { BASE_API_URL } from "@/config/api"; | |||
| import { cache } from "react"; | |||
| import { ScheduleType } from "."; | |||
| import { DetailedProdScheduleLineBomMaterialResult, DetailedProdScheduleLineResult, ScheduleType } from "."; | |||
| import { revalidateTag } from "next/cache"; | |||
| export interface SearchProdSchedule { | |||
| scheduleAt?: string; | |||
| @@ -31,11 +32,29 @@ export interface ProdScheduleResultByPage { | |||
| records: ProdScheduleResult[]; | |||
| } | |||
| export interface ReleaseDetailProdScheduleInputs { | |||
| export interface ReleaseProdScheduleInputs { | |||
| id: number; | |||
| demandQty: number; | |||
| } | |||
| export interface ReleaseProdScheduleResponse { | |||
| id: number; | |||
| code: string; | |||
| entity: { | |||
| prodScheduleLines: DetailedProdScheduleLineResult[]; | |||
| }; | |||
| message: string; | |||
| } | |||
| export interface SaveProdScheduleResponse { | |||
| id: number; | |||
| code: string; | |||
| entity: { | |||
| bomMaterials: DetailedProdScheduleLineBomMaterialResult[] | |||
| }; | |||
| message: string; | |||
| } | |||
| export const fetchProdSchedules = cache( | |||
| async (data: SearchProdSchedule | null) => { | |||
| const params = convertObjToURLSearchParams<SearchProdSchedule>(data); | |||
| @@ -79,16 +98,32 @@ export const testDetailedSchedule = cache(async () => { | |||
| ); | |||
| }); | |||
| export const releaseProdScheduleLine = cache(async (data: ReleaseDetailProdScheduleInputs) => { | |||
| return serverFetchJson( | |||
| `${BASE_API_URL}/productionSchedule/releaseLine`, | |||
| export const releaseProdScheduleLine = cache(async (data: ReleaseProdScheduleInputs) => { | |||
| const response = serverFetchJson<ReleaseProdScheduleResponse>( | |||
| `${BASE_API_URL}/productionSchedule/detail/detailed/releaseLine`, | |||
| { | |||
| method: "POST", | |||
| body: JSON.stringify(data), | |||
| headers: { "Content-Type": "application/json" }, | |||
| } | |||
| ); | |||
| revalidateTag("prodSchedules"); | |||
| return response; | |||
| }) | |||
| export const saveProdScheduleLine = cache(async (data: ReleaseProdScheduleInputs) => { | |||
| const response = serverFetchJson<SaveProdScheduleResponse>( | |||
| `${BASE_API_URL}/productionSchedule/detail/detailed/save`, | |||
| { | |||
| method: "POST", | |||
| body: JSON.stringify(data), | |||
| headers: { "Content-Type": "application/json" }, | |||
| next: { | |||
| tags: ["prodSchedules"], | |||
| }, | |||
| } | |||
| ) | |||
| ); | |||
| revalidateTag("prodSchedules"); | |||
| return response; | |||
| }) | |||
| @@ -93,8 +93,11 @@ export interface DetailedProdScheduleLineResult { | |||
| name: string; | |||
| type: string; | |||
| demandQty: number; | |||
| bomOutputQty: number; | |||
| prodTimeInMinute: DetailedProdScheduleLineProdTimeResult[]; | |||
| priority: number; | |||
| approved: boolean; | |||
| proportion: number; | |||
| } | |||
| export interface DetailedProdScheduleLineBomMaterialResult { | |||
| @@ -23,6 +23,7 @@ export const moneyFormatter = new Intl.NumberFormat("en-HK", { | |||
| export const decimalFormatter = new Intl.NumberFormat("en-HK", { | |||
| minimumFractionDigits: 2, | |||
| maximumFractionDigits: 2, | |||
| }); | |||
| export const integerFormatter = new Intl.NumberFormat("en-HK", {}); | |||
| @@ -7,6 +7,7 @@ import { | |||
| FormProvider, | |||
| SubmitErrorHandler, | |||
| SubmitHandler, | |||
| useFieldArray, | |||
| useForm, | |||
| } from "react-hook-form"; | |||
| import { | |||
| @@ -26,7 +27,10 @@ import DetailInfoCard from "@/components/DetailedScheduleDetail/DetailInfoCard"; | |||
| import ViewByFGDetails, { | |||
| // FGRecord, | |||
| } from "@/components/DetailedScheduleDetail/ViewByFGDetails"; | |||
| import { DetailedProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; | |||
| import { DetailedProdScheduleLineResult, DetailedProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; | |||
| import { releaseProdScheduleLine, saveProdScheduleLine } from "@/app/api/scheduling/actions"; | |||
| import useUploadContext from "../UploadProvider/useUploadContext"; | |||
| import ArrowBackIcon from '@mui/icons-material/ArrowBack'; | |||
| // temp interface input | |||
| // export interface SaveDetailedSchedule { | |||
| @@ -59,14 +63,20 @@ const DetailedScheduleDetailView: React.FC<Props> = ({ | |||
| const [tabIndex, setTabIndex] = useState(0); | |||
| const { t } = useTranslation("schedule"); | |||
| const router = useRouter(); | |||
| const [isEdit, setIsEdit] = useState(false); | |||
| const [isEdit, setIsEdit] = useState(false); | |||
| const { setIsUploading } = useUploadContext() | |||
| // console.log(typeId) | |||
| const formProps = useForm<DetailedProdScheduleResult>({ | |||
| defaultValues: defaultValues | |||
| }); | |||
| const errors = formProps.formState.errors; | |||
| const lineFormProps = useFieldArray<DetailedProdScheduleResult>({ | |||
| control: formProps.control, | |||
| name: "prodScheduleLines" | |||
| }) | |||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||
| (_e, newValue) => { | |||
| setTabIndex(newValue); | |||
| @@ -74,6 +84,10 @@ const [isEdit, setIsEdit] = useState(false); | |||
| [], | |||
| ); | |||
| // const calNewProportion = useCallback((demandQty: number, bomOutputQty: number) => { | |||
| // return ((demandQty ?? 0) / (bomOutputQty ?? 1)).toFixed(2) | |||
| // }, []) | |||
| // const [pagingController, setPagingController] = useState({ | |||
| // pageNum: 1, | |||
| // pageSize: 10, | |||
| @@ -81,7 +95,7 @@ const [isEdit, setIsEdit] = useState(false); | |||
| // }); | |||
| const handleCancel = () => { | |||
| router.replace(`/scheduling/Detail`); | |||
| router.replace(`/scheduling/detailed`); | |||
| }; | |||
| const onSubmit = useCallback<SubmitHandler<DetailedProdScheduleResult>>( | |||
| @@ -106,7 +120,7 @@ const [isEdit, setIsEdit] = useState(false); | |||
| // multiple tabs | |||
| const onSubmitError = useCallback<SubmitErrorHandler<DetailedProdScheduleResult>>( | |||
| (errors) => {}, | |||
| (errors) => { }, | |||
| [], | |||
| ); | |||
| @@ -114,10 +128,69 @@ const [isEdit, setIsEdit] = useState(false); | |||
| setIsEdit(!isEdit); | |||
| }; | |||
| const onReleaseClick = useCallback(() => { | |||
| const onReleaseClick = useCallback(async (row: DetailedProdScheduleLineResult) => { | |||
| setIsUploading(true) | |||
| try { | |||
| const response = await releaseProdScheduleLine({ | |||
| id: row.id, | |||
| demandQty: row.demandQty | |||
| }) | |||
| if (response) { | |||
| const index = formProps.getValues("prodScheduleLines").findIndex(ele => ele.id == row.id) | |||
| // console.log(index, formProps.getValues(`prodScheduleLines.${index}.approved`)) | |||
| // formProps.setValue(`prodScheduleLines.${index}.approved`, true) | |||
| // formProps.setValue(`prodScheduleLines.${index}.jobNo`, response.code) | |||
| formProps.setValue(`prodScheduleLines`, response.entity.prodScheduleLines.sort((a, b) => b.priority - a.priority)) | |||
| // console.log(index, formProps.getValues(`prodScheduleLines.${index}.approved`)) | |||
| } | |||
| setIsUploading(false) | |||
| } catch (e) { | |||
| console.log(e) | |||
| setIsUploading(false) | |||
| } | |||
| }, []) | |||
| const [tempValue, setTempValue] = useState<string | number | null>(null) | |||
| const onEditClick = useCallback((rowId: number) => { | |||
| const row = formProps.getValues("prodScheduleLines").find(ele => ele.id == rowId) | |||
| if (row) { | |||
| setTempValue(row.demandQty) | |||
| } | |||
| }, []) | |||
| const handleEditChange = useCallback((rowId: number, fieldName: keyof DetailedProdScheduleLineResult, newValue: number | string) => { | |||
| const index = formProps.getValues("prodScheduleLines").findIndex(ele => ele.id == rowId) | |||
| formProps.setValue(`prodScheduleLines.${index}.demandQty`, Number(newValue)) | |||
| }, []) | |||
| const onSaveClick = useCallback(async (row: DetailedProdScheduleLineResult) => { | |||
| setIsUploading(true) | |||
| try { | |||
| const response = await saveProdScheduleLine({ | |||
| id: row.id, | |||
| demandQty: row.demandQty | |||
| }) | |||
| if (response) { | |||
| const index = formProps.getValues("prodScheduleLines").findIndex(ele => ele.id == row.id) | |||
| formProps.setValue(`prodScheduleLines.${index}.bomMaterials`, response.entity.bomMaterials) | |||
| } | |||
| setIsUploading(false) | |||
| } catch (e) { | |||
| console.log(e) | |||
| setIsUploading(false) | |||
| } | |||
| }, []) | |||
| const onCancelClick = useCallback(async (rowId: number) => { | |||
| // if (tempValue) { | |||
| const index = formProps.getValues("prodScheduleLines").findIndex(ele => ele.id == rowId) | |||
| formProps.setValue(`prodScheduleLines.${index}.demandQty`, Number(tempValue)) | |||
| // } | |||
| }, [tempValue]) | |||
| return ( | |||
| <> | |||
| <FormProvider {...formProps}> | |||
| @@ -133,9 +206,10 @@ const [isEdit, setIsEdit] = useState(false); | |||
| {/*</Grid>*/} | |||
| <DetailInfoCard | |||
| // recordDetails={formProps.formState.defaultValues} | |||
| isEditing={isEdit} | |||
| // isEditing={isEdit} | |||
| isEditing={false} | |||
| /> | |||
| <Stack | |||
| {/* <Stack | |||
| direction="row" | |||
| justifyContent="space-between" | |||
| flexWrap="wrap" | |||
| @@ -144,13 +218,13 @@ const [isEdit, setIsEdit] = useState(false); | |||
| <Button | |||
| variant="contained" | |||
| onClick={onClickEdit} | |||
| // startIcon={<Add />} | |||
| //LinkComponent={Link} | |||
| //href="qcCategory/create" | |||
| // startIcon={<Add />} | |||
| //LinkComponent={Link} | |||
| //href="qcCategory/create" | |||
| > | |||
| {isEdit ? t("Save") : t("Edit")} | |||
| </Button> | |||
| </Stack> | |||
| </Stack> */} | |||
| {/* <Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable"> | |||
| <Tab label={t("View By FG") + (tabIndex === 0 ? " (Selected)" : "")} iconPosition="end" /> | |||
| @@ -162,24 +236,32 @@ const [isEdit, setIsEdit] = useState(false); | |||
| </Typography> | |||
| )} | |||
| {/* {tabIndex === 0 && <ViewByFGDetails isEdit={isEdit} apiRef={apiRef} />} */} | |||
| <ViewByFGDetails isEdit={isEdit} apiRef={apiRef} onReleaseClick={onReleaseClick} type={type}/> | |||
| <ViewByFGDetails | |||
| isEdit={true} | |||
| apiRef={apiRef} | |||
| onReleaseClick={onReleaseClick} | |||
| onEditClick={onEditClick} | |||
| handleEditChange={handleEditChange} | |||
| onSaveClick={onSaveClick} | |||
| onCancelClick={onCancelClick} | |||
| type={type} /> | |||
| {/* {tabIndex === 1 && <ViewByBomDetails isEdit={isEdit} apiRef={apiRef} isHideButton={true} />} */} | |||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||
| <Button | |||
| {/* <Button | |||
| name="submit" | |||
| variant="contained" | |||
| startIcon={<Check />} | |||
| type="submit" | |||
| // disabled={submitDisabled} | |||
| // disabled={submitDisabled} | |||
| > | |||
| {isEditMode ? t("Save") : t("Confirm")} | |||
| </Button> | |||
| </Button> */} | |||
| <Button | |||
| variant="outlined" | |||
| startIcon={<Close />} | |||
| startIcon={<ArrowBackIcon />} | |||
| onClick={handleCancel} | |||
| > | |||
| {t("Cancel")} | |||
| {t("Back")} | |||
| </Button> | |||
| </Stack> | |||
| </Stack> | |||
| @@ -23,7 +23,11 @@ type Props = { | |||
| apiRef: MutableRefObject<GridApiCommunity>; | |||
| isEdit: boolean; | |||
| type: ScheduleType; | |||
| onReleaseClick: () => void; | |||
| onReleaseClick: (item: DetailedProdScheduleLineResult) => void; | |||
| onEditClick: (rowId: number) => void; | |||
| handleEditChange: (rowId: number, fieldName: keyof DetailedProdScheduleLineResult, newValue: number | string) => void; | |||
| onSaveClick: (item: DetailedProdScheduleLineResult) => void; | |||
| onCancelClick: (rowId: number) => void; | |||
| }; | |||
| // export type FGRecord = { | |||
| @@ -35,7 +39,7 @@ type Props = { | |||
| // purchaseQty?: number; | |||
| // }; | |||
| const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick }) => { | |||
| const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick, onEditClick, handleEditChange, onSaveClick, onCancelClick }) => { | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| @@ -43,8 +47,10 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick | |||
| const { | |||
| getValues, | |||
| watch, | |||
| formState: { errors, defaultValues, touchedFields }, | |||
| } = useFormContext<DetailedProdScheduleResult>(); | |||
| // const apiRef = useGridApiRef(); | |||
| // const [pagingController, setPagingController] = useState([ | |||
| @@ -128,6 +134,9 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick | |||
| field: "type", | |||
| label: t("type"), | |||
| type: "read-only", | |||
| renderCell: (row) => { | |||
| return t(row.type); | |||
| }, | |||
| // editable: true, | |||
| }, | |||
| // { | |||
| @@ -148,7 +157,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick | |||
| { | |||
| field: "demandQty", | |||
| label: t("Demand Qty"), | |||
| type: "input", | |||
| type: "input-number", | |||
| style: { | |||
| textAlign: "right", | |||
| }, | |||
| @@ -202,23 +211,28 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick | |||
| /> | |||
| </Grid> */} | |||
| {/* {dayPeriod.map((date, index) => ( */} | |||
| <Grid item xs={12}> | |||
| {/* <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| <Grid item xs={12}> | |||
| {/* <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| {`${t("FG Demand Date")}: ${date}`} | |||
| </Typography> */} | |||
| <ScheduleTable<DetailedProdScheduleLineResult> | |||
| type={type} | |||
| // items={fakeRecords[index]} // Use the corresponding records for the day | |||
| items={getValues("prodScheduleLines")} // Use the corresponding records for the day | |||
| columns={columns} | |||
| // setPagingController={updatePagingController} | |||
| // pagingController={pagingController[index]} | |||
| isAutoPaging={false} | |||
| isEditable={true} | |||
| isEdit={isEdit} | |||
| hasCollapse={true} | |||
| /> | |||
| </Grid> | |||
| <ScheduleTable<DetailedProdScheduleLineResult> | |||
| type={type} | |||
| // items={fakeRecords[index]} // Use the corresponding records for the day | |||
| items={getValues("prodScheduleLines")} // Use the corresponding records for the day | |||
| columns={columns} | |||
| // setPagingController={updatePagingController} | |||
| // pagingController={pagingController[index]} | |||
| isAutoPaging={false} | |||
| isEditable={true} | |||
| isEdit={isEdit} | |||
| hasCollapse={true} | |||
| onReleaseClick={onReleaseClick} | |||
| onEditClick={onEditClick} | |||
| handleEditChange={handleEditChange} | |||
| onSaveClick={onSaveClick} | |||
| onCancelClick={onCancelClick} | |||
| /> | |||
| </Grid> | |||
| {/* ))} */} | |||
| </Grid> | |||
| ); | |||
| @@ -5,6 +5,7 @@ import React, { | |||
| DetailedHTMLProps, | |||
| HTMLAttributes, | |||
| useEffect, | |||
| useRef, | |||
| useState, | |||
| } from "react"; | |||
| import Paper from "@mui/material/Paper"; | |||
| @@ -37,6 +38,7 @@ import { | |||
| ScheduleType, | |||
| } from "@/app/api/scheduling"; | |||
| import { defaultPagingController } from "../SearchResults/SearchResults"; | |||
| import { useFormContext } from "react-hook-form"; | |||
| export interface ResultWithId { | |||
| id: string | number; | |||
| @@ -81,6 +83,11 @@ interface Props<T extends ResultWithId> { | |||
| isEditable: boolean; | |||
| hasCollapse: boolean; | |||
| type: ScheduleType; | |||
| onReleaseClick?: (item: T) => void; | |||
| onEditClick?: (rowId: number) => void; | |||
| handleEditChange?: (rowId: number, fieldName: keyof T, newValue: number | string) => void; | |||
| onSaveClick?: (item: T) => void; | |||
| onCancelClick?: (rowId: number) => void; | |||
| } | |||
| function ScheduleTable<T extends ResultWithId>({ | |||
| @@ -95,15 +102,23 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| isEdit = false, | |||
| isEditable = true, | |||
| hasCollapse = false, | |||
| onReleaseClick = undefined, | |||
| onEditClick = undefined, | |||
| handleEditChange = undefined, | |||
| onSaveClick = undefined, | |||
| onCancelClick = undefined, | |||
| }: Props<T>) { | |||
| const [page, setPage] = useState(0); | |||
| const [rowsPerPage, setRowsPerPage] = useState(10); | |||
| const [editingRowId, setEditingRowId] = useState<number | null>(null); | |||
| const [editedItems, setEditedItems] = useState<T[]>(items); | |||
| const { t } = useTranslation("schedule"); | |||
| console.log(items) | |||
| useEffect(() => { | |||
| setEditedItems(items); | |||
| }, [items]); | |||
| const handleChangePage = (_event: unknown, newPage: number) => { | |||
| setPage(newPage); | |||
| if (setPagingController && pagingController) { | |||
| @@ -132,24 +147,42 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| const handleEditClick = (id: number) => { | |||
| setEditingRowId(id); | |||
| if (onEditClick) { | |||
| onEditClick(id) | |||
| } | |||
| }; | |||
| const handleSaveClick = (item: T) => { | |||
| setEditingRowId(null); | |||
| // Call API or any save logic here | |||
| setEditedItems((prev) => | |||
| prev.map((row) => (row.id === item.id ? { ...row } : row)), | |||
| ); | |||
| if (onSaveClick) { | |||
| onSaveClick(item) | |||
| } else { | |||
| setEditedItems((prev) => | |||
| prev.map((row) => (row.id === item.id ? { ...row } : row)), | |||
| ); | |||
| } | |||
| }; | |||
| const handleReleaseClick = (item: T) => { | |||
| if (onReleaseClick) { | |||
| onReleaseClick(item) | |||
| } | |||
| } | |||
| const handleInputChange = ( | |||
| id: number, | |||
| field: keyof T, | |||
| value: string | number[], | |||
| // value: string | number[], | |||
| value: string | number, | |||
| ) => { | |||
| setEditedItems((prev) => | |||
| prev.map((item) => (item.id === id ? { ...item, [field]: value } : item)), | |||
| ); | |||
| if (handleEditChange) { | |||
| handleEditChange(id, field, value) | |||
| } else { | |||
| setEditedItems((prev) => | |||
| prev.map((item) => (item.id === id ? { ...item, [field]: value } : item)), | |||
| ); | |||
| } | |||
| }; | |||
| const handleDeleteClick = (id: number) => { | |||
| @@ -157,6 +190,14 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| setEditedItems((prev) => prev.filter((item) => item.id !== id)); | |||
| }; | |||
| const handleCancelClick = (id: number) => { | |||
| if (onCancelClick) { | |||
| onCancelClick(id) | |||
| } | |||
| setEditingRowId(null) | |||
| } | |||
| useEffect(() => { | |||
| console.log("[debug] isEdit in table", isEdit); | |||
| //TODO: switch all record to not in edit mode and save the changes | |||
| @@ -188,7 +229,15 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| <TableRow hover tabIndex={-1} key={row.id}> | |||
| {isDetailedType(type) && ( | |||
| <TableCell> | |||
| <IconButton disabled={!isEdit}> | |||
| <IconButton | |||
| color="primary" | |||
| disabled={ | |||
| // !(row as unknown as DetailedProdScheduleLineResult).bomMaterials.every(ele => (ele.availableQty ?? 0) >= (ele.demandQty ?? 0)) | |||
| // || | |||
| editingRowId === row.id | |||
| || (row as unknown as DetailedProdScheduleLineResult).approved} | |||
| onClick={() => handleReleaseClick(row)} | |||
| > | |||
| <PlayCircleOutlineIcon /> | |||
| </IconButton> | |||
| </TableCell> | |||
| @@ -208,7 +257,7 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| {isDetailedType(type) && isEditable && ( | |||
| <IconButton | |||
| disabled={!isEdit} | |||
| onClick={() => setEditingRowId(null)} | |||
| onClick={() => handleCancelClick(row.id as number)} | |||
| > | |||
| <CancelIcon /> | |||
| </IconButton> | |||
| @@ -232,20 +281,20 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| <> | |||
| {isDetailedType(type) && isEditable && ( | |||
| <IconButton | |||
| disabled={!isEdit} | |||
| disabled={!isEdit || (row as unknown as DetailedProdScheduleLineResult).approved} | |||
| onClick={() => handleEditClick(row.id as number)} | |||
| > | |||
| <EditIcon /> | |||
| </IconButton> | |||
| )} | |||
| {isDetailedType(type) && isEditable && ( | |||
| {/* {isDetailedType(type) && isEditable && ( | |||
| <IconButton | |||
| disabled={!isEdit} | |||
| onClick={() => handleDeleteClick(row.id as number)} | |||
| > | |||
| <DeleteIcon /> | |||
| </IconButton> | |||
| )} | |||
| )} */} | |||
| {hasCollapse && ( | |||
| <IconButton | |||
| aria-label="expand row" | |||
| @@ -286,6 +335,29 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| } | |||
| /> | |||
| ); | |||
| case "input-number": | |||
| return ( | |||
| <TextField | |||
| type="number" | |||
| hiddenLabel={true} | |||
| fullWidth | |||
| defaultValue={row[columnName] as string} | |||
| onChange={(e) => { | |||
| handleInputChange( | |||
| row.id as number, | |||
| columnName, | |||
| e.target.value, | |||
| ) | |||
| }} | |||
| // onChange={(e) => | |||
| // handleInputChange( | |||
| // row.id as number, | |||
| // columnName, | |||
| // e.target.value, | |||
| // ) | |||
| // } | |||
| /> | |||
| ); | |||
| // case 'multi-select': | |||
| // //TODO: May need update if use | |||
| // return ( | |||
| @@ -297,7 +369,9 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| // /> | |||
| // ); | |||
| case "read-only": | |||
| return <span>{row[columnName] as string}</span>; | |||
| return column.renderCell ? ( | |||
| <div style={column.style}>{column.renderCell(row)}</div> | |||
| ) : <span>{row[columnName] as string}</span>; | |||
| default: | |||
| return null; // Handle any default case if needed | |||
| } | |||
| @@ -330,8 +404,8 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| <BomMaterialTable | |||
| type={type} | |||
| bomMaterial={ | |||
| isDetailedType(type) ? (row as unknown as RoughProdScheduleLineResultByFg).bomMaterials | |||
| : (row as unknown as DetailedProdScheduleLineResult).bomMaterials | |||
| isDetailedType(type) ? (row as unknown as RoughProdScheduleLineResultByFg).bomMaterials | |||
| : (row as unknown as DetailedProdScheduleLineResult).bomMaterials | |||
| } | |||
| /> | |||
| </TableCell> | |||
| @@ -82,5 +82,6 @@ | |||
| "mat": "物料", | |||
| "Product Count(s)": "產品數量", | |||
| "Schedule Period To": "排程期間至", | |||
| "Overall": "總計" | |||
| "Overall": "總計", | |||
| "Back": "返回" | |||
| } | |||