diff --git a/src/app/(main)/scheduling/detail/page.tsx b/src/app/(main)/scheduling/detail/page.tsx index e09bca2..e7c2762 100644 --- a/src/app/(main)/scheduling/detail/page.tsx +++ b/src/app/(main)/scheduling/detail/page.tsx @@ -2,7 +2,6 @@ // import DetailSchedule from "@/components/DetailSchedule"; // import { getServerI18n } from "@/i18n"; -import { TypeEnum } from "../../../../app/utils/typeEnum"; import DetailSchedule from "../../../../components/DetailSchedule"; import { getServerI18n } from "../../../../i18n"; import { I18nProvider } from "@/i18n"; @@ -16,8 +15,8 @@ export const metadata: Metadata = { }; const DetailScheduling: React.FC = async () => { - const project = TypeEnum.PRODUCT - const { t } = await getServerI18n("detailScheduling"); + const { t } = await getServerI18n("schedule"); + const type = "detailed" // preloadClaims(); return ( @@ -32,9 +31,9 @@ const DetailScheduling: React.FC = async () => { {t("Detail Scheduling")} - + }> - + diff --git a/src/app/(main)/scheduling/rough/page.tsx b/src/app/(main)/scheduling/rough/page.tsx index ff7915b..e949b21 100644 --- a/src/app/(main)/scheduling/rough/page.tsx +++ b/src/app/(main)/scheduling/rough/page.tsx @@ -1,6 +1,7 @@ // import { TypeEnum } from "@/app/utils/typeEnum"; // import RoughSchedule from "@/components/RoughSchedule"; // import { getServerI18n, I18nProvider } from "@/i18n"; +import { testRoughSchedule } from "@/app/api/scheduling/actions"; import { TypeEnum } from "../../../../app/utils/typeEnum"; import RoughSchedule from "../../../../components/RoughSchedule"; import { getServerI18n, I18nProvider } from "../../../../i18n"; @@ -22,6 +23,10 @@ const roughScheduling: React.FC = async () => { const type = "rough" // preloadClaims(); + // async function testingRoughSchedule() { + // await testRoughSchedule(); + // } + return ( <> { {t("Demand Forecast")} - + {/* */} + variant="contained" + startIcon={} + onClick={() => testingRoughSchedule} + > + {t("Test Rough Scheduling")} + */} - + }> - + diff --git a/src/app/api/scheduling/actions.ts b/src/app/api/scheduling/actions.ts index e7d2ebb..d230ec0 100644 --- a/src/app/api/scheduling/actions.ts +++ b/src/app/api/scheduling/actions.ts @@ -4,13 +4,14 @@ 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 "."; export interface SearchProdSchedule { scheduleAt?: string; schedulePeriod?: string; schedulePeriodTo?: string; totalEstProdCount?: number; - type?: "manual" | "detailed" | "rough"; + types?: ScheduleType[]; pageSize?: number; pageNum?: number; } @@ -40,4 +41,24 @@ export const fetchProdSchedules = cache(async (data: SearchProdSchedule | null) tags: ["prodSchedules"] } }) +}) + +export const testRoughSchedule = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/productionSchedule/testRoughSchedule`, { + method: "GET", + headers: { "Content-Type": "application/json" }, + next: { + tags: ["prodSchedules"] + } + }) +}) + +export const testDetailSchedule = cache(async () => { + return serverFetchJson(`${BASE_API_URL}/productionSchedule/testDetailSchedule`, { + method: "GET", + headers: { "Content-Type": "application/json" }, + next: { + tags: ["prodSchedules"] + } + }) }) \ No newline at end of file diff --git a/src/app/api/scheduling/index.ts b/src/app/api/scheduling/index.ts index d64b5b3..0d24edc 100644 --- a/src/app/api/scheduling/index.ts +++ b/src/app/api/scheduling/index.ts @@ -3,9 +3,9 @@ import { BASE_API_URL } from "@/config/api" import { cache } from "react" import "server-only" -export type ScheduleType = "rough" | "detail"; +export type ScheduleType = "all" | "rough" | "detailed" | "manual"; -export interface ProdScheduleResult { +export interface RoughProdScheduleResult { id: number; scheduleAt: number[]; schedulePeriod: number[]; @@ -13,13 +13,13 @@ export interface ProdScheduleResult { totalEstProdCount: number; totalFGType: number; type: string; - prodScheduleLinesByFg: ProdScheduleLineResultByFg[]; - prodScheduleLinesByFgByDate: { [assignDate: number]: ProdScheduleLineResultByFg[] }; - prodScheduleLinesByBom: ProdScheduleLineResultByBom[]; - prodScheduleLinesByBomByDate: { [assignDate: number]: ProdScheduleLineResultByBomByDate[] }; + prodScheduleLinesByFg: RoughProdScheduleLineResultByFg[]; + prodScheduleLinesByFgByDate: { [assignDate: number]: RoughProdScheduleLineResultByFg[] }; + prodScheduleLinesByBom: RoughProdScheduleLineResultByBom[]; + prodScheduleLinesByBomByDate: { [assignDate: number]: RoughProdScheduleLineResultByBomByDate[] }; } -export interface ProdScheduleLineResultByFg { +export interface RoughProdScheduleLineResultByFg { id: number; code: string; name: string; @@ -30,10 +30,10 @@ export interface ProdScheduleLineResultByFg { estCloseBal: number; priority: number; assignDate: number; - bomMaterials: ProdScheduleLineBomMaterialResult[]; + bomMaterials: RoughProdScheduleLineBomMaterialResult[]; } -export interface ProdScheduleLineBomMaterialResult { +export interface RoughProdScheduleLineBomMaterialResult { id: number; code: string; name: string; @@ -43,7 +43,7 @@ export interface ProdScheduleLineBomMaterialResult { uomName: string; } -export interface ProdScheduleLineResultByBom { +export interface RoughProdScheduleLineResultByBom { id: number, code: string, name: string, @@ -60,7 +60,7 @@ export interface ProdScheduleLineResultByBom { uomName: string, } -export interface ProdScheduleLineResultByBomByDate { +export interface RoughProdScheduleLineResultByBomByDate { id: number, code: string, name: string, @@ -72,7 +72,7 @@ export interface ProdScheduleLineResultByBomByDate { } export const fetchProdScheduleDetail = cache(async (id: number) => { - return serverFetchJson(`${BASE_API_URL}/productionSchedule/detail/${id}`, { + return serverFetchJson(`${BASE_API_URL}/productionSchedule/detail/${id}`, { method: "GET", headers: { "Content-Type": "application/json" }, next: { diff --git a/src/components/DetailSchedule/DetailScheduleSearchView.tsx b/src/components/DetailSchedule/DetailScheduleSearchView.tsx index a1d16c7..05effed 100644 --- a/src/components/DetailSchedule/DetailScheduleSearchView.tsx +++ b/src/components/DetailSchedule/DetailScheduleSearchView.tsx @@ -1,67 +1,75 @@ "use client"; -import React, {useCallback, useEffect, useMemo, useState} from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import SearchBox, { Criterion } from "../SearchBox"; -import { ItemsResult} from "@/app/api/settings/item"; +import { ItemsResult } from "@/app/api/settings/item"; import SearchResults, { Column } from "../SearchResults"; import { EditNote } from "@mui/icons-material"; import { useRouter, useSearchParams } from "next/navigation"; import { GridDeleteIcon } from "@mui/x-data-grid"; import { TypeEnum } from "@/app/utils/typeEnum"; import axios from "axios"; -import {BASE_API_URL, NEXT_PUBLIC_API_URL} from "@/config/api"; +import { BASE_API_URL, NEXT_PUBLIC_API_URL } from "@/config/api"; import { useTranslation } from "react-i18next"; import axiosInstance from "@/app/(main)/axios/axiosInstance"; import Qs from 'qs'; import EditableSearchResults from "@/components/SearchResults/EditableSearchResults"; // Make sure to import Qs +import { ScheduleType } from "@/app/api/scheduling"; +import { ProdScheduleResult, SearchProdSchedule, fetchProdSchedules } from "@/app/api/scheduling/actions"; +import { defaultPagingController } from "../SearchResults/SearchResults"; +import { arrayToDateString, decimalFormatter } from "@/app/utils/formatUtil"; +import dayjs from "dayjs"; +import { uniqBy } from "lodash"; // may need move to "index" or "actions" -type RecordStructure = { - id: number, - scheduledPeriod: string, - scheduledAt: string, - productCount: number, -}; +// type RecordStructure = { +// id: number, +// scheduledPeriod: string, +// scheduledAt: string, +// productCount: number, +// }; type Props = { - records: RecordStructure[]; + type: ScheduleType; + // records: RecordStructure[]; + defaultInputs: SearchProdSchedule; }; -type SearchQuery = Partial>; +type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; -const DSOverview: React.FC = ({ records }) => { - const [filteredItems, setFilteredItems] = useState(records ?? []); - const { t } = useTranslation("detailScheduling"); - +const DSOverview: React.FC = ({ type, defaultInputs }) => { + const [filteredSchedules, setFilteredSchedules] = useState([]); + const { t } = useTranslation("schedule"); + const router = useRouter(); - const [filterObj, setFilterObj] = useState({}); - const [tempSelectedValue, setTempSelectedValue] = useState({}); - const [pagingController, setPagingController] = useState({ - pageNum: 1, - pageSize: 10, - totalCount: 0, - }) - - const [mode, redirPath] = useMemo(() => { - // var typeId = TypeEnum.CONSUMABLE_ID - let title = ""; - const mode = "Search"; - let redirPath = ""; - title = "Product"; - redirPath = "/scheduling/detail"; - return [mode, redirPath]; - }, []); + // const [filterObj, setFilterObj] = useState({}); + // const [tempSelectedValue, setTempSelectedValue] = useState({}); + const [pagingController, setPagingController] = useState(defaultPagingController) + const [totalCount, setTotalCount] = useState(0) + const [inputs, setInputs] = useState(defaultInputs) + const typeOptions = [ + { + value: "detailed", + label: t("Detailed") + }, + { + value: "manual", + label: t("Manual") + }, + ] const searchCriteria: Criterion[] = useMemo( - () => - [ - { label: t("Schedule Period"), paramName: "scheduledPeriod", type: "dateRange" }, - { label: t("Scheduled At"), paramName: "scheduledAt", type: "dateRange" }, - { label: t("Product Count"), paramName: "productCount", type: "text" }, + () => { + var searchCriteria: Criterion[] = [ + { label: t("Schedule Period"), label2: t("Schedule Period To"), paramName: "schedulePeriod", type: "dateRange" }, + { label: t("Production Date"), paramName: "scheduleAt", type: "date" }, + { label: t("Product Count"), paramName: "totalEstProdCount", type: "text" }, + { label: t("Type"), paramName: "types", type: "autocomplete", options: typeOptions }, ] - , - [t, records] + return searchCriteria + }, + [t] ); // const onDetailClick = useCallback( @@ -71,36 +79,54 @@ const DSOverview: React.FC = ({ records }) => { // [router] // ); - const onDeleteClick = useCallback( - (item: ItemsResult) => {}, - [router] - ); + // const onDeleteClick = useCallback( + // (item: ItemsResult) => {}, + // [router] + // ); - const onDetailClick = (record: any) => { + const onDetailClick = (record: ProdScheduleResult) => { console.log("[debug] record", record); router.push(`/scheduling/detail/edit?id=${record.id}`); } - const columns = useMemo[]>( + const columns = useMemo[]>( () => [ { name: "id", label: t("Details"), - onClick: (record)=>onDetailClick(record), + onClick: (record) => onDetailClick(record), buttonIcon: , }, { - name: "scheduledPeriod", + name: "schedulePeriod", label: t("Demand Forecast Period"), + renderCell: (params) => { + return `${arrayToDateString(params.schedulePeriod)} - ${arrayToDateString(params.schedulePeriodTo)}` + } }, { - name: "scheduledAt", - label: t("Scheduled At"), + name: "scheduleAt", + label: t("Production Date"), + renderCell: (params) => { + return arrayToDateString(params.scheduleAt) + } }, { - name: "productCount", + name: "totalEstProdCount", label: t("Product Count(s)"), + headerAlign: "right", + align: "right", + renderCell: (params) => { + return decimalFormatter.format(params.totalEstProdCount) + } + }, + { + name: "type", + label: t("Type"), + renderCell: (params) => { + return t(params.type) + } }, // { // name: "action", @@ -109,92 +135,138 @@ const DSOverview: React.FC = ({ records }) => { // onClick: onDeleteClick, // }, ], - [filteredItems] + [filteredSchedules] ); + const refetchData = useCallback(async (query: Record | SearchProdSchedule, actionType: "reset" | "search" | "paging") => { + // console.log(query) + const defaultTypes = ["detailed", "manual"] + const convertedTypes = (query.types == undefined || typeof (query.types) == "string" ? query.types?.toLowerCase() == "all" ? defaultTypes : [query.types] + : query.types.some((ele) => ele.toLowerCase() === "all") ? defaultTypes : query.types) as ScheduleType[]; + + console.log(convertedTypes) + console.log(query.types) + const params: SearchProdSchedule = { + scheduleAt: dayjs(query?.scheduleAt).isValid() ? query?.scheduleAt : undefined, + schedulePeriod: dayjs(query?.schedulePeriod).isValid() ? query?.schedulePeriod : undefined, + schedulePeriodTo: dayjs(query?.schedulePeriodTo).isValid() ? query?.schedulePeriodTo : undefined, + totalEstProdCount: query?.totalEstProdCount ? Number(query?.totalEstProdCount) : undefined, + types: convertedTypes, + pageNum: pagingController.pageNum - 1, + pageSize: pagingController.pageSize + } + const response = await fetchProdSchedules(params) + + // console.log(response) + if (response) { + setTotalCount(response.total) + switch (actionType) { + case "reset": + case "search": + setFilteredSchedules(() => response.records) + break; + case "paging": + setFilteredSchedules((fs) => uniqBy([...fs, ...response.records], "id")) + break; + } + } + }, [pagingController, setPagingController]) + useEffect(() => { - refetchData(filterObj); + refetchData(inputs, "paging") + }, [pagingController]) - }, [filterObj, pagingController.pageNum, pagingController.pageSize]); + // useEffect(() => { + // refetchData(filterObj); - const refetchData = async (filterObj: SearchQuery | null) => { + // }, [filterObj, pagingController.pageNum, pagingController.pageSize]); - const authHeader = axiosInstance.defaults.headers['Authorization']; - if (!authHeader) { - return; // Exit the function if the token is not set - } + // const refetchData = async (filterObj: SearchQuery | null) => { - const params ={ - pageNum: pagingController.pageNum, - pageSize: pagingController.pageSize, - ...filterObj, - ...tempSelectedValue, - } + // const authHeader = axiosInstance.defaults.headers['Authorization']; + // if (!authHeader) { + // return; // Exit the function if the token is not set + // } - try { - const response = await axiosInstance.get(`${NEXT_PUBLIC_API_URL}/items/getRecordByPage`, { - params, - paramsSerializer: (params) => { - return Qs.stringify(params, { arrayFormat: 'repeat' }); - }, - }); - //setFilteredItems(response.data.records); - setFilteredItems([ - { - id: 1, - scheduledPeriod: "2025-05-11 to 2025-05-17", - scheduledAt: "2025-05-07", - productCount: 13, - }, - { - id: 2, - scheduledPeriod: "2025-05-18 to 2025-05-24", - scheduledAt: "2025-05-14", - productCount: 15, - }, - { - id: 3, - scheduledPeriod: "2025-05-25 to 2025-05-31", - scheduledAt: "2025-05-21", - productCount: 13, - }, - ]) - setPagingController({ - ...pagingController, - totalCount: response.data.total - }) - return response; // Return the data from the response - } catch (error) { - console.error('Error fetching items:', error); - throw error; // Rethrow the error for further handling - } - }; + // const params = { + // pageNum: pagingController.pageNum, + // pageSize: pagingController.pageSize, + // ...filterObj, + // ...tempSelectedValue, + // } + + // try { + // const response = await axiosInstance.get(`${NEXT_PUBLIC_API_URL}/items/getRecordByPage`, { + // params, + // paramsSerializer: (params) => { + // return Qs.stringify(params, { arrayFormat: 'repeat' }); + // }, + // }); + // //setFilteredItems(response.data.records); + // setFilteredItems([ + // { + // id: 1, + // scheduledPeriod: "2025-05-11 to 2025-05-17", + // scheduledAt: "2025-05-07", + // productCount: 13, + // }, + // { + // id: 2, + // scheduledPeriod: "2025-05-18 to 2025-05-24", + // scheduledAt: "2025-05-14", + // productCount: 15, + // }, + // { + // id: 3, + // scheduledPeriod: "2025-05-25 to 2025-05-31", + // scheduledAt: "2025-05-21", + // productCount: 13, + // }, + // ]) + // setPagingController({ + // ...pagingController, + // totalCount: response.data.total + // }) + // return response; // Return the data from the response + // } catch (error) { + // console.error('Error fetching items:', error); + // throw error; // Rethrow the error for further handling + // } + // }; const onReset = useCallback(() => { //setFilteredItems(items ?? []); - setFilterObj({}); - setTempSelectedValue({}); - refetchData(null); - }, [records]); + // setFilterObj({}); + // setTempSelectedValue({}); + refetchData(inputs, "reset"); + }, []); return ( <> { - setFilterObj({ - ...query - }) + setInputs(() => ( + { + scheduleAt: query?.scheduleAt, + schedulePeriod: query?.schedulePeriod, + schedulePeriodTo: query?.schedulePeriodTo, + totalEstProdCount: Number(query?.totalEstProdCount), + types: query.types as unknown as ScheduleType[] + } + )) + refetchData(query, "search") }} onReset={onReset} /> - - items={filteredItems} + + items={filteredSchedules} columns={columns} setPagingController={setPagingController} pagingController={pagingController} - isAutoPaging={false} - // hasCollapse={false} + totalCount={totalCount} + // isAutoPaging={false} + // hasCollapse={false} /> ); diff --git a/src/components/DetailSchedule/DetailScheduleWrapper.tsx b/src/components/DetailSchedule/DetailScheduleWrapper.tsx index e1a6c7f..f7e34d7 100644 --- a/src/components/DetailSchedule/DetailScheduleWrapper.tsx +++ b/src/components/DetailSchedule/DetailScheduleWrapper.tsx @@ -1,14 +1,26 @@ import React from "react"; import {DetailScheduleLoading} from "./DetailScheduleLoading"; import DSOverview from "./DetailScheduleSearchView"; +import { ScheduleType } from "@/app/api/scheduling"; +import { SearchProdSchedule } from "@/app/api/scheduling/actions"; interface SubComponents { Loading: typeof DetailScheduleLoading; } -const DetailScheduleWrapper: React.FC & SubComponents = async () => { +type Props = { + type: ScheduleType +} + +const DetailScheduleWrapper: React.FC & SubComponents = async ({ + type +}) => { + + const defaultInputs: SearchProdSchedule = { + types: ["detailed", "manual"] + } - return ; + return ; }; DetailScheduleWrapper.Loading = DetailScheduleLoading; diff --git a/src/components/DetailScheduleDetail/DetailInfoCard.tsx b/src/components/DetailScheduleDetail/DetailInfoCard.tsx index f830358..6140763 100644 --- a/src/components/DetailScheduleDetail/DetailInfoCard.tsx +++ b/src/components/DetailScheduleDetail/DetailInfoCard.tsx @@ -23,11 +23,14 @@ import { SaveDetailSchedule } from "./DetailScheudleDetailView"; // temp interface input type Props = { - recordDetails: SaveDetailSchedule; + // recordDetails: SaveDetailSchedule; isEditing: boolean; }; -const DetailInfoCard: React.FC = ({ recordDetails, isEditing }) => { +const DetailInfoCard: React.FC = ({ + // recordDetails, + isEditing + }) => { const { t, i18n: { language }, @@ -39,12 +42,12 @@ const DetailInfoCard: React.FC = ({ recordDetails, isEditing }) => { formState: { errors, defaultValues, touchedFields }, } = useFormContext(); - const [details, setDetails] = useState(null); + const [details, setDetails] = useState(undefined); useEffect(() => { - console.log("[debug] record details", recordDetails) - setDetails(recordDetails); - }, [recordDetails]) + console.log("[debug] record details", defaultValues) + setDetails(defaultValues as SaveDetailSchedule); + }, [defaultValues]) useEffect(() => { console.log("[debug] isEdit", isEditing) diff --git a/src/components/DetailScheduleDetail/DetailScheudleDetailView.tsx b/src/components/DetailScheduleDetail/DetailScheudleDetailView.tsx index 559bbad..80bb273 100644 --- a/src/components/DetailScheduleDetail/DetailScheudleDetailView.tsx +++ b/src/components/DetailScheduleDetail/DetailScheudleDetailView.tsx @@ -36,7 +36,7 @@ type Props = { isEditMode: boolean; // type: TypeEnum; defaultValues: Partial | undefined; - qcChecks: ItemQc[] + // qcChecks: ItemQc[] }; const DetailScheduleDetailView: React.FC = ({ @@ -89,7 +89,7 @@ const DetailScheduleDetailView: React.FC = ({ productionDate: "2025-05-07", totalJobOrders: 13, totalProductionQty: 21000, - }, + } as SaveDetailSchedule, }); const errors = formProps.formState.errors; @@ -157,7 +157,7 @@ const DetailScheduleDetailView: React.FC = ({ {/* */} {/**/} = ({ items }) => { ...filterObj, }; try { - const response = await axiosInstance.get( + const response = await axiosInstance.get( `${NEXT_PUBLIC_API_URL}/items/getRecordByPage`, { params } ); diff --git a/src/components/RoughSchedule/RoughSchedileSearchView.tsx b/src/components/RoughSchedule/RoughSchedileSearchView.tsx index cb22ee5..bc3f778 100644 --- a/src/components/RoughSchedule/RoughSchedileSearchView.tsx +++ b/src/components/RoughSchedule/RoughSchedileSearchView.tsx @@ -19,6 +19,7 @@ import { arrayToDateString, decimalFormatter } from "@/app/utils/formatUtil"; import { isEqual, uniqBy } from "lodash"; import dayjs from "dayjs"; import { defaultPagingController } from "../SearchResults/SearchResults"; +import { ScheduleType } from "@/app/api/scheduling"; // type RecordStructure ={ // id: number, @@ -27,12 +28,12 @@ import { defaultPagingController } from "../SearchResults/SearchResults"; // }; type Props = { - type: SearchProdSchedule["type"]; + type: ScheduleType; // initProdSchedules: ProdScheduleResultByPage; defaultInputs: SearchProdSchedule; }; -type SearchQuery = Partial>; +type SearchQuery = Partial>; type SearchParamNames = keyof SearchQuery; const RSOverview: React.FC = ({ type, defaultInputs }) => { @@ -64,12 +65,12 @@ const RSOverview: React.FC = ({ type, defaultInputs }) => { // [router] // ); - const onDeleteClick = useCallback( - (item: ItemsResult) => { }, - [router] - ); + // const onDeleteClick = useCallback( + // (item: ItemsResult) => { }, + // [router] + // ); - const onDetailClick = (record: any) => { + const onDetailClick = (record: ProdScheduleResult) => { console.log("[debug] record", record); router.push(`/scheduling/rough/edit?id=${record.id}`); } @@ -127,7 +128,7 @@ const RSOverview: React.FC = ({ type, defaultInputs }) => { schedulePeriod: dayjs(query?.schedulePeriod).isValid() ? query?.schedulePeriod : undefined, schedulePeriodTo: dayjs(query?.schedulePeriodTo).isValid() ? query?.schedulePeriodTo : undefined, totalEstProdCount: query?.totalEstProdCount ? Number(query?.totalEstProdCount) : undefined, - type: "rough", + types: ["rough"], pageNum: pagingController.pageNum - 1, pageSize: pagingController.pageSize } diff --git a/src/components/RoughSchedule/RoughScheduleWrapper.tsx b/src/components/RoughSchedule/RoughScheduleWrapper.tsx index 04e59bc..490e27c 100644 --- a/src/components/RoughSchedule/RoughScheduleWrapper.tsx +++ b/src/components/RoughSchedule/RoughScheduleWrapper.tsx @@ -2,13 +2,14 @@ import { fetchAllItems } from "@/app/api/settings/item"; import { RoughScheduleLoading } from "./RoughScheduleLoading"; import RSOverview from "./RoughSchedileSearchView"; import { SearchProdSchedule, fetchProdSchedules } from "@/app/api/scheduling/actions"; +import { ScheduleType } from "@/app/api/scheduling"; interface SubComponents { Loading: typeof RoughScheduleLoading; } type Props = { - type: SearchProdSchedule["type"] + type: ScheduleType }; const RoughScheduleWrapper: React.FC & SubComponents = async ( @@ -18,7 +19,7 @@ const RoughScheduleWrapper: React.FC & SubComponents = async ( ) => { // console.log(type) const defaultInputs: SearchProdSchedule = { - type: "rough" + types: ["rough"] } // const [ diff --git a/src/components/RoughScheduleDetail/DetailInfoCard.tsx b/src/components/RoughScheduleDetail/DetailInfoCard.tsx index 9dfb6f4..5cefcff 100644 --- a/src/components/RoughScheduleDetail/DetailInfoCard.tsx +++ b/src/components/RoughScheduleDetail/DetailInfoCard.tsx @@ -18,7 +18,7 @@ import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; import { TypeEnum } from "@/app/utils/typeEnum"; import { NumberInputProps } from "@/components/CreateItem/NumberInputProps"; import { arrayToDateString, integerFormatter } from "@/app/utils/formatUtil"; -import { ProdScheduleResult } from "@/app/api/scheduling"; +import { RoughProdScheduleResult } from "@/app/api/scheduling"; import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; @@ -38,12 +38,12 @@ const DetailInfoCard: React.FC = ({ isEditing }) => { getValues, watch, formState: { errors, defaultValues, touchedFields }, - } = useFormContext(); + } = useFormContext(); // const [details, setDetails] = useState(null); useEffect(() => { - console.log("[debug] record details", getValues) + console.log("[debug] record details", getValues()) // setDetails(recordDetails); }, [getValues]) diff --git a/src/components/RoughScheduleDetail/RoughScheduleDetailWrapper.tsx b/src/components/RoughScheduleDetail/RoughScheduleDetailWrapper.tsx index 1c0d399..694a3c3 100644 --- a/src/components/RoughScheduleDetail/RoughScheduleDetailWrapper.tsx +++ b/src/components/RoughScheduleDetail/RoughScheduleDetailWrapper.tsx @@ -15,7 +15,7 @@ type Props = { const RoughScheduleDetailWrapper: React.FC & SubComponents = async ({ id, type }) => { const prodSchedule = id ? await fetchProdScheduleDetail(id) : undefined - + return ( | undefined; + defaultValues: Partial | undefined; // qcChecks: ItemQc[] }; @@ -42,7 +42,6 @@ const RoughScheduleDetailView: React.FC = ({ // console.log(type) const apiRef = useGridApiRef(); const params = useSearchParams() - console.log(params.get("id")) const [serverError, setServerError] = useState(""); const [tabIndex, setTabIndex] = useState(0); const { t } = useTranslation("schedule") @@ -51,7 +50,7 @@ const RoughScheduleDetailView: React.FC = ({ //const title = "Demand Forecast Detail" // console.log(typeId) - const formProps = useForm({ + const formProps = useForm({ defaultValues: defaultValues ? defaultValues : { }, }); @@ -88,7 +87,7 @@ const RoughScheduleDetailView: React.FC = ({ router.replace(`/scheduling/rough`); }; - const onSubmit = useCallback>( + const onSubmit = useCallback>( async (data, event) => { let hasErrors = false; console.log(errors) @@ -109,7 +108,7 @@ const RoughScheduleDetailView: React.FC = ({ ); // multiple tabs - const onSubmitError = useCallback>( + const onSubmitError = useCallback>( (errors) => { }, [] ); diff --git a/src/components/RoughScheduleDetail/ViewByBomDetails.tsx b/src/components/RoughScheduleDetail/ViewByBomDetails.tsx index 8e8b814..d02661e 100644 --- a/src/components/RoughScheduleDetail/ViewByBomDetails.tsx +++ b/src/components/RoughScheduleDetail/ViewByBomDetails.tsx @@ -21,7 +21,7 @@ import { RiceBowl } from "@mui/icons-material"; // import EditableSearchResults, { Column } from "@/components/SearchResults/EditableSearchResults"; import { decimalFormatter } from "@/app/utils/formatUtil"; import { GridRenderCellParams } from "@mui/x-data-grid"; -import { ProdScheduleLineResultByBom, ProdScheduleLineResultByBomByDate, ProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; +import { RoughProdScheduleLineResultByBom, RoughProdScheduleLineResultByBomByDate, RoughProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; import ScheduleTable from "@/components/ScheduleTable"; import { Column } from "@/components/ScheduleTable/ScheduleTable"; @@ -63,7 +63,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) control, getValues, formState: { errors, defaultValues, touchedFields }, - } = useFormContext(); + } = useFormContext(); // const apiRef = useGridApiRef(); const { fields } = useFieldArray({ @@ -165,7 +165,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.availableQty) == "number") { return decimalFormatter.format(row.availableQty) } @@ -180,7 +180,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.totalDemandQty) == "number") { return decimalFormatter.format(row.totalDemandQty) } @@ -194,7 +194,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.demandQty1) == "number") { return decimalFormatter.format(row.demandQty1) } @@ -208,7 +208,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.demandQty2) == "number") { return decimalFormatter.format(row.demandQty2) } @@ -222,7 +222,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.demandQty3) == "number") { return decimalFormatter.format(row.demandQty3) } @@ -236,7 +236,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.demandQty4) == "number") { return decimalFormatter.format(row.demandQty4) } @@ -249,7 +249,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.demandQty5) == "number") { return decimalFormatter.format(row.demandQty5) } @@ -263,7 +263,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.demandQty6) == "number") { return decimalFormatter.format(row.demandQty6) } @@ -277,7 +277,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBom) => { + renderCell: (row: RoughProdScheduleLineResultByBom) => { if (typeof (row.demandQty7) == "number") { return decimalFormatter.format(row.demandQty7) } @@ -288,7 +288,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) [t] ); - const columns = useMemo[]>( + const columns = useMemo[]>( () => [ { field: "code", @@ -317,7 +317,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBomByDate) => { + renderCell: (row: RoughProdScheduleLineResultByBomByDate) => { if (typeof (row.availableQty) == "number") { return decimalFormatter.format(row.availableQty) } @@ -331,7 +331,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) style: { textAlign: "right", }, - renderCell: (row: ProdScheduleLineResultByBomByDate) => { + renderCell: (row: RoughProdScheduleLineResultByBomByDate) => { if (typeof (row.demandQty) == "number") { return decimalFormatter.format(row.demandQty) } @@ -342,15 +342,13 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) [] ); - console.log(getValues("prodScheduleLinesByBom")) - return ( {t("Material Demand List (7 Days)")} - + // index={7} type={type} items={getValues("prodScheduleLinesByBom")} @@ -368,7 +366,7 @@ const ViewByBomDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) {`${t("Material Demand Date")}: ${date}`} - + // index={index} type={type} items={getValues("prodScheduleLinesByBomByDate")[index + 1]} // Use the corresponding records for the day diff --git a/src/components/RoughScheduleDetail/ViewByFGDetails.tsx b/src/components/RoughScheduleDetail/ViewByFGDetails.tsx index bd31a71..22d27e5 100644 --- a/src/components/RoughScheduleDetail/ViewByFGDetails.tsx +++ b/src/components/RoughScheduleDetail/ViewByFGDetails.tsx @@ -21,7 +21,7 @@ import { RiceBowl } from "@mui/icons-material"; import ScheduleTable from "@/components/ScheduleTable"; import { Column } from "@/components/ScheduleTable/ScheduleTable"; import { arrayToDayjs, dayjsToDateString, decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; -import { ProdScheduleLineResultByFg, ProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; +import { RoughProdScheduleLineResultByFg, RoughProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; type Props = { apiRef: MutableRefObject @@ -50,7 +50,7 @@ const ViewByFGDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) = control, getValues, formState: { errors, defaultValues, touchedFields }, - } = useFormContext(); + } = useFormContext(); const { fields } = useFieldArray({ control, @@ -125,7 +125,7 @@ const ViewByFGDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) = }); }; - const columns = useMemo[]>( + const columns = useMemo[]>( () => [ { field: "code", @@ -177,7 +177,7 @@ const ViewByFGDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) = [] ); - const overallColumns = useMemo[]>( + const overallColumns = useMemo[]>( () => [ { field: "code", @@ -270,7 +270,7 @@ const ViewByFGDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) = {t("FG Demand List (7 Days)")} - + // index={7} type={type} items={getValues("prodScheduleLinesByFg")} @@ -288,9 +288,9 @@ const ViewByFGDetails: React.FC = ({ apiRef, isEdit, type, dayPeriod }) = {`${t("FG Demand Date")}: ${date}`} - + type={type} - items={getValues("prodScheduleLinesByFgByDate")[index + 1]} // Use the corresponding records for the day + items={getValues("prodScheduleLinesByFgByDate") && Object.entries(getValues("prodScheduleLinesByFgByDate")).length > 0 ? getValues("prodScheduleLinesByFgByDate")[index + 1] : []} // Use the corresponding records for the day columns={columns} setPagingController={updatePagingController} pagingController={pagingController[index]} diff --git a/src/components/ScheduleTable/BomMaterialTable.tsx b/src/components/ScheduleTable/BomMaterialTable.tsx index e3fc4d8..fb2fe7d 100644 --- a/src/components/ScheduleTable/BomMaterialTable.tsx +++ b/src/components/ScheduleTable/BomMaterialTable.tsx @@ -36,14 +36,14 @@ import { useSearchParams } from "next/navigation"; import { decimalFormatter } from "@/app/utils/formatUtil"; import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; import HighlightOffIcon from '@mui/icons-material/HighlightOff'; -import { ProdScheduleLineBomMaterialResult, ScheduleType } from "@/app/api/scheduling"; +import { RoughProdScheduleLineBomMaterialResult, ScheduleType } from "@/app/api/scheduling"; interface ResultWithId { id: number; } interface Props { - bomMaterial: ProdScheduleLineBomMaterialResult[]; + bomMaterial: RoughProdScheduleLineBomMaterialResult[]; type: ScheduleType } diff --git a/src/components/ScheduleTable/ScheduleTable.tsx b/src/components/ScheduleTable/ScheduleTable.tsx index 21ea100..ae0733b 100644 --- a/src/components/ScheduleTable/ScheduleTable.tsx +++ b/src/components/ScheduleTable/ScheduleTable.tsx @@ -23,7 +23,7 @@ import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'; import { useTranslation } from "react-i18next"; -import { ProdScheduleLineBomMaterialResult, ProdScheduleLineResultByFg, ProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; +import { RoughProdScheduleLineBomMaterialResult, RoughProdScheduleLineResultByFg, RoughProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; export interface ResultWithId { id: string | number; @@ -144,10 +144,10 @@ function ScheduleTable({ return type === "rough"; } - function isDetailType( + function isDetailedType( type: ScheduleType - ): type is "detail" { - return type === "detail"; + ): type is "detailed" { + return type === "detailed"; } function Row(props: { row: T }) { @@ -157,7 +157,7 @@ function ScheduleTable({ return ( <> - {isDetailType(type) && + {isDetailedType(type) && @@ -167,12 +167,12 @@ function ScheduleTable({ {(editingRowId === row.id) ? ( <> { - isDetailType(type) && isEditable && handleSaveClick(row)}> + isDetailedType(type) && isEditable && handleSaveClick(row)}> } { - isDetailType(type) && isEditable && setEditingRowId(null)}> + isDetailedType(type) && isEditable && setEditingRowId(null)}> } @@ -190,13 +190,13 @@ function ScheduleTable({ ) : ( <> { - isDetailType(type) && isEditable && handleEditClick(row.id as number)}> } { - isDetailType(type) && isEditable && handleDeleteClick(row.id as number)}> @@ -278,7 +278,7 @@ function ScheduleTable({ @@ -298,7 +298,7 @@ function ScheduleTable({ - {isDetailType(type) && {t("Release")}} + {isDetailedType(type) && {t("Release")}} {(isEditable || hasCollapse) && {t("Actions")}} {/* Action Column Header */} {columns.map((column, idx) => ( diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx index d386fcf..cfa215c 100644 --- a/src/components/SearchBox/SearchBox.tsx +++ b/src/components/SearchBox/SearchBox.tsx @@ -196,7 +196,7 @@ function SearchBox({ {c.type === "text" && ( ({ )} {c.type === "multi-select" && ( ({ )} {c.type === "select" && ( - {c.label} + {t(c.label)} @@ -312,7 +312,7 @@ function SearchBox({ ); }} - renderInput={(params) => } + renderInput={(params) => } /> )} {c.type === "dateRange" && ( @@ -324,7 +324,7 @@ function SearchBox({ @@ -339,7 +339,7 @@ function SearchBox({ @@ -356,7 +356,7 @@ function SearchBox({ diff --git a/src/components/SearchResults/EditableSearchResults.tsx b/src/components/SearchResults/EditableSearchResults.tsx index f90a715..0f211a8 100644 --- a/src/components/SearchResults/EditableSearchResults.tsx +++ b/src/components/SearchResults/EditableSearchResults.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useEffect, useState } from "react"; +import React, { CSSProperties, useEffect, useState } from "react"; import Paper from "@mui/material/Paper"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; @@ -23,6 +23,7 @@ import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'; import { useTranslation } from "react-i18next"; +import { RoughProdScheduleLineResultByFg } from "@/app/api/scheduling"; export interface ResultWithId { id: string | number; } @@ -32,8 +33,9 @@ interface BaseColumn { label: string; type: string; options?: T[]; - renderCell?: (T) => void; - style?: Partial & { [propName: string]: string }; + renderCell?: (params: T) => React.ReactNode; + style?: Partial & { [propName: string]: string } & CSSProperties; + // style?: Partial & { [propName: string]: string }; } interface ColumnWithAction extends BaseColumn { @@ -52,7 +54,11 @@ interface Props { noWrapper?: boolean, setPagingController: (value: { pageNum: number; pageSize: number; totalCount: number, index?: number }) => void, pagingController: { pageNum: number; pageSize: number; totalCount: number }, - isAutoPaging: boolean + isAutoPaging: boolean, + index: any, + isEdit: any, + isEditable: any, + hasCollapse: any, } function EditableSearchResults({ @@ -78,7 +84,7 @@ function EditableSearchResults({ }, [items]) const handleChangePage = (_event: unknown, newPage: number) => { setPage(newPage); - setPagingController({ ...pagingController, pageNum: newPage + 1 }, (index ?? -1)); + // setPagingController({ ...pagingController, pageNum: newPage + 1 }, (index ?? -1)); }; const handleChangeRowsPerPage = (event: React.ChangeEvent) => { @@ -134,7 +140,7 @@ function EditableSearchResults({ <> - + @@ -208,15 +214,15 @@ function EditableSearchResults({ onChange={(e) => handleInputChange(row.id as number, columnName, e.target.value)} /> ); - case 'multi-select': - return ( - handleInputChange(row.id as number, columnName, selectedValues)} - /> - ); + // case 'multi-select': + // return ( + // handleInputChange(row.id as number, columnName, selectedValues)} + // /> + // ); case 'read-only': return ( @@ -253,7 +259,7 @@ function EditableSearchResults({ diff --git a/src/i18n/zh/schedule.json b/src/i18n/zh/schedule.json index da93d77..7967104 100644 --- a/src/i18n/zh/schedule.json +++ b/src/i18n/zh/schedule.json @@ -10,7 +10,8 @@ "Demand Forecast Detail": "需求預測詳情", "Details": "詳情", "Schedule": "排程", - "Schedule Period": "排程期間", + "Schedule Period": "排程時期", + "Schedule Period To": "排程時期至", "Schedule Detail": "排程詳情", "Schedule At": "排程時間", "Search": "搜尋", @@ -23,7 +24,7 @@ "CODE": "編號", "Product Count": "產品數量", "Scheduled At": "排程時間", - "Demand Forecast Period": "需求預測期間", + "Demand Forecast Period": "需求預測時期", "FG & Material Demand Forecast Detail": "成品及物料需求預測詳情", "FG & Material Demand Forecast": "成品及物料需求預測", "Total Estimated Demand Qty": "總預估需求量",