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/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..ff5b994 100644 --- a/src/components/DetailScheduleDetail/DetailInfoCard.tsx +++ b/src/components/DetailScheduleDetail/DetailInfoCard.tsx @@ -23,7 +23,7 @@ import { SaveDetailSchedule } from "./DetailScheudleDetailView"; // temp interface input type Props = { - recordDetails: SaveDetailSchedule; + recordDetails: any; isEditing: boolean; }; diff --git a/src/components/DetailScheduleDetail/DetailScheudleDetailView.tsx b/src/components/DetailScheduleDetail/DetailScheudleDetailView.tsx index 9728f45..2e337d5 100644 --- a/src/components/DetailScheduleDetail/DetailScheudleDetailView.tsx +++ b/src/components/DetailScheduleDetail/DetailScheudleDetailView.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { useTranslation } from "react-i18next"; import { - SaveDetailSchedule, + // SaveDetailSchedule, saveItem, } from "@/app/api/settings/item/actions"; import { 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({