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