# Conflicts: # src/components/DetailScheduleDetail/DetailScheudleDetailView.tsxmaster
| @@ -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")} | |||
| </Typography> | |||
| </Stack> | |||
| <I18nProvider namespaces={["detailScheduling", "items", "common","schedule"]}> | |||
| <I18nProvider namespaces={["schedule", "common"]}> | |||
| <Suspense fallback={<DetailSchedule.Loading />}> | |||
| <DetailSchedule /> | |||
| <DetailSchedule type={type}/> | |||
| </Suspense> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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 ( | |||
| <> | |||
| <Stack | |||
| @@ -33,19 +38,18 @@ const roughScheduling: React.FC = async () => { | |||
| <Typography variant="h4" marginInlineEnd={2}> | |||
| {t("Demand Forecast")} | |||
| </Typography> | |||
| {/* <Button | |||
| variant="contained" | |||
| startIcon={<Add />} | |||
| LinkComponent={Link} | |||
| href="product/create" | |||
| > | |||
| {t("Create product")} | |||
| </Button> */} | |||
| variant="contained" | |||
| startIcon={<Add />} | |||
| onClick={() => testingRoughSchedule} | |||
| > | |||
| {t("Test Rough Scheduling")} | |||
| </Button> */} | |||
| </Stack> | |||
| <I18nProvider namespaces={["schedule", "common","items","project"]}> | |||
| <I18nProvider namespaces={["schedule", "common"]}> | |||
| <Suspense fallback={<RoughSchedule.Loading />}> | |||
| <RoughSchedule type={type}/> | |||
| <RoughSchedule type={type} /> | |||
| </Suspense> | |||
| </I18nProvider> | |||
| </> | |||
| @@ -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"] | |||
| } | |||
| }) | |||
| }) | |||
| @@ -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<ProdScheduleResult>(`${BASE_API_URL}/productionSchedule/detail/${id}`, { | |||
| return serverFetchJson<RoughProdScheduleResult>(`${BASE_API_URL}/productionSchedule/detail/${id}`, { | |||
| method: "GET", | |||
| headers: { "Content-Type": "application/json" }, | |||
| next: { | |||
| @@ -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<Omit<RecordStructure, "id">>; | |||
| type SearchQuery = Partial<Omit<SearchProdSchedule, "id" | "pageSize" | "pageNum">>; | |||
| type SearchParamNames = keyof SearchQuery; | |||
| const DSOverview: React.FC<Props> = ({ records }) => { | |||
| const [filteredItems, setFilteredItems] = useState<RecordStructure[]>(records ?? []); | |||
| const { t } = useTranslation("detailScheduling"); | |||
| const DSOverview: React.FC<Props> = ({ type, defaultInputs }) => { | |||
| const [filteredSchedules, setFilteredSchedules] = useState<ProdScheduleResult[]>([]); | |||
| 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<SearchParamNames>[] = 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<SearchParamNames>[] = [ | |||
| { 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<Props> = ({ 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<Column<RecordStructure>[]>( | |||
| const columns = useMemo<Column<ProdScheduleResult>[]>( | |||
| () => [ | |||
| { | |||
| name: "id", | |||
| label: t("Details"), | |||
| onClick: (record)=>onDetailClick(record), | |||
| onClick: (record) => onDetailClick(record), | |||
| buttonIcon: <EditNote />, | |||
| }, | |||
| { | |||
| 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<Props> = ({ records }) => { | |||
| // onClick: onDeleteClick, | |||
| // }, | |||
| ], | |||
| [filteredItems] | |||
| [filteredSchedules] | |||
| ); | |||
| const refetchData = useCallback(async (query: Record<SearchParamNames, string> | 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<ItemsResult[]>(`${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<ItemsResult[]>(`${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 ( | |||
| <> | |||
| <SearchBox | |||
| criteria={searchCriteria} | |||
| onSearch={(query) => { | |||
| 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} | |||
| /> | |||
| <SearchResults<RecordStructure> | |||
| items={filteredItems} | |||
| <SearchResults<ProdScheduleResult> | |||
| items={filteredSchedules} | |||
| columns={columns} | |||
| setPagingController={setPagingController} | |||
| pagingController={pagingController} | |||
| isAutoPaging={false} | |||
| // hasCollapse={false} | |||
| totalCount={totalCount} | |||
| // isAutoPaging={false} | |||
| // hasCollapse={false} | |||
| /> | |||
| </> | |||
| ); | |||
| @@ -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<Props> & SubComponents = async ({ | |||
| type | |||
| }) => { | |||
| const defaultInputs: SearchProdSchedule = { | |||
| types: ["detailed", "manual"] | |||
| } | |||
| return <DSOverview records={[]} />; | |||
| return <DSOverview type={type} defaultInputs={defaultInputs} />; | |||
| }; | |||
| DetailScheduleWrapper.Loading = DetailScheduleLoading; | |||
| @@ -23,11 +23,14 @@ import { SaveDetailSchedule } from "./DetailScheudleDetailView"; | |||
| // temp interface input | |||
| type Props = { | |||
| recordDetails: SaveDetailSchedule; | |||
| // recordDetails: SaveDetailSchedule; | |||
| isEditing: boolean; | |||
| }; | |||
| const DetailInfoCard: React.FC<Props> = ({ recordDetails, isEditing }) => { | |||
| const DetailInfoCard: React.FC<Props> = ({ | |||
| // recordDetails, | |||
| isEditing | |||
| }) => { | |||
| const { | |||
| t, | |||
| i18n: { language }, | |||
| @@ -39,12 +42,12 @@ const DetailInfoCard: React.FC<Props> = ({ recordDetails, isEditing }) => { | |||
| formState: { errors, defaultValues, touchedFields }, | |||
| } = useFormContext<SaveDetailSchedule>(); | |||
| const [details, setDetails] = useState<SaveDetailSchedule|null>(null); | |||
| const [details, setDetails] = useState<SaveDetailSchedule | undefined>(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) | |||
| @@ -36,7 +36,7 @@ type Props = { | |||
| isEditMode: boolean; | |||
| // type: TypeEnum; | |||
| defaultValues: Partial<SaveDetailSchedule> | undefined; | |||
| qcChecks: ItemQc[] | |||
| // qcChecks: ItemQc[] | |||
| }; | |||
| const DetailScheduleDetailView: React.FC<Props> = ({ | |||
| @@ -89,7 +89,7 @@ const DetailScheduleDetailView: React.FC<Props> = ({ | |||
| productionDate: "2025-05-07", | |||
| totalJobOrders: 13, | |||
| totalProductionQty: 21000, | |||
| }, | |||
| } as SaveDetailSchedule, | |||
| }); | |||
| const errors = formProps.formState.errors; | |||
| @@ -157,7 +157,7 @@ const DetailScheduleDetailView: React.FC<Props> = ({ | |||
| {/* </Typography>*/} | |||
| {/*</Grid>*/} | |||
| <DetailInfoCard | |||
| recordDetails={formProps.formState.defaultValues as SaveDetailSchedule} | |||
| // recordDetails={formProps.formState.defaultValues} | |||
| isEditing={isEdit} | |||
| /> | |||
| <Stack | |||
| @@ -2,7 +2,7 @@ | |||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||
| import SearchBox, { Criterion } from "../SearchBox"; | |||
| import { ItemsResult } from "@/app/api/settings/item"; | |||
| import { ItemsResult, ItemsResultResponse } from "@/app/api/settings/item"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import SearchResults, { Column } from "../SearchResults"; | |||
| import { EditNote } from "@mui/icons-material"; | |||
| @@ -85,7 +85,7 @@ const ItemsSearch: React.FC<Props> = ({ items }) => { | |||
| ...filterObj, | |||
| }; | |||
| try { | |||
| const response = await axiosInstance.get<ItemsResult[]>( | |||
| const response = await axiosInstance.get<ItemsResultResponse>( | |||
| `${NEXT_PUBLIC_API_URL}/items/getRecordByPage`, | |||
| { params } | |||
| ); | |||
| @@ -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<Omit<SearchProdSchedule, "id" | "type" | "pageSize" | "pageNum">>; | |||
| type SearchQuery = Partial<Omit<SearchProdSchedule, "id" | "types" | "pageSize" | "pageNum">>; | |||
| type SearchParamNames = keyof SearchQuery; | |||
| const RSOverview: React.FC<Props> = ({ type, defaultInputs }) => { | |||
| @@ -64,12 +65,12 @@ const RSOverview: React.FC<Props> = ({ 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<Props> = ({ 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 | |||
| } | |||
| @@ -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<Props> & SubComponents = async ( | |||
| @@ -18,7 +19,7 @@ const RoughScheduleWrapper: React.FC<Props> & SubComponents = async ( | |||
| ) => { | |||
| // console.log(type) | |||
| const defaultInputs: SearchProdSchedule = { | |||
| type: "rough" | |||
| types: ["rough"] | |||
| } | |||
| // const [ | |||
| @@ -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<Props> = ({ isEditing }) => { | |||
| getValues, | |||
| watch, | |||
| formState: { errors, defaultValues, touchedFields }, | |||
| } = useFormContext<ProdScheduleResult>(); | |||
| } = useFormContext<RoughProdScheduleResult>(); | |||
| // const [details, setDetails] = useState(null); | |||
| useEffect(() => { | |||
| console.log("[debug] record details", getValues) | |||
| console.log("[debug] record details", getValues()) | |||
| // setDetails(recordDetails); | |||
| }, [getValues]) | |||
| @@ -15,7 +15,7 @@ type Props = { | |||
| const RoughScheduleDetailWrapper: React.FC<Props> & SubComponents = async ({ id, type }) => { | |||
| const prodSchedule = id ? await fetchProdScheduleDetail(id) : undefined | |||
| return ( | |||
| <RoughScheduleDetailView | |||
| isEditMode={Boolean(id)} | |||
| @@ -24,13 +24,13 @@ import ViewByFGDetails from "@/components/RoughScheduleDetail/ViewByFGDetails"; | |||
| import ViewByBomDetails from "@/components/RoughScheduleDetail/ViewByBomDetails"; | |||
| import ScheduleTable from "@/components/ScheduleTable"; | |||
| import { Column } from "@/components/ScheduleTable/ScheduleTable"; | |||
| import { ProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; | |||
| import { RoughProdScheduleResult, ScheduleType } from "@/app/api/scheduling"; | |||
| import { arrayToDayjs, dayjsToDateString } from "@/app/utils/formatUtil"; | |||
| type Props = { | |||
| isEditMode: boolean; | |||
| type: ScheduleType; | |||
| defaultValues: Partial<ProdScheduleResult> | undefined; | |||
| defaultValues: Partial<RoughProdScheduleResult> | undefined; | |||
| // qcChecks: ItemQc[] | |||
| }; | |||
| @@ -42,7 +42,6 @@ const RoughScheduleDetailView: React.FC<Props> = ({ | |||
| // 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<Props> = ({ | |||
| //const title = "Demand Forecast Detail" | |||
| // console.log(typeId) | |||
| const formProps = useForm<ProdScheduleResult>({ | |||
| const formProps = useForm<RoughProdScheduleResult>({ | |||
| defaultValues: defaultValues ? defaultValues : { | |||
| }, | |||
| }); | |||
| @@ -88,7 +87,7 @@ const RoughScheduleDetailView: React.FC<Props> = ({ | |||
| router.replace(`/scheduling/rough`); | |||
| }; | |||
| const onSubmit = useCallback<SubmitHandler<ProdScheduleResult>>( | |||
| const onSubmit = useCallback<SubmitHandler<RoughProdScheduleResult>>( | |||
| async (data, event) => { | |||
| let hasErrors = false; | |||
| console.log(errors) | |||
| @@ -109,7 +108,7 @@ const RoughScheduleDetailView: React.FC<Props> = ({ | |||
| ); | |||
| // multiple tabs | |||
| const onSubmitError = useCallback<SubmitErrorHandler<ProdScheduleResult>>( | |||
| const onSubmitError = useCallback<SubmitErrorHandler<RoughProdScheduleResult>>( | |||
| (errors) => { }, | |||
| [] | |||
| ); | |||
| @@ -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<Props> = ({ apiRef, isEdit, type, dayPeriod }) | |||
| control, | |||
| getValues, | |||
| formState: { errors, defaultValues, touchedFields }, | |||
| } = useFormContext<ProdScheduleResult>(); | |||
| } = useFormContext<RoughProdScheduleResult>(); | |||
| // const apiRef = useGridApiRef(); | |||
| const { fields } = useFieldArray({ | |||
| @@ -165,7 +165,7 @@ const ViewByBomDetails: React.FC<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ 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<Props> = ({ apiRef, isEdit, type, dayPeriod }) | |||
| [t] | |||
| ); | |||
| const columns = useMemo<Column<ProdScheduleLineResultByBomByDate>[]>( | |||
| const columns = useMemo<Column<RoughProdScheduleLineResultByBomByDate>[]>( | |||
| () => [ | |||
| { | |||
| field: "code", | |||
| @@ -317,7 +317,7 @@ const ViewByBomDetails: React.FC<Props> = ({ 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<Props> = ({ 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<Props> = ({ apiRef, isEdit, type, dayPeriod }) | |||
| [] | |||
| ); | |||
| console.log(getValues("prodScheduleLinesByBom")) | |||
| return ( | |||
| <Grid container spacing={2}> | |||
| <Grid item xs={12} key={"all"}> | |||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| {t("Material Demand List (7 Days)")} | |||
| </Typography> | |||
| <ScheduleTable<ProdScheduleLineResultByBom> | |||
| <ScheduleTable<RoughProdScheduleLineResultByBom> | |||
| // index={7} | |||
| type={type} | |||
| items={getValues("prodScheduleLinesByBom")} | |||
| @@ -368,7 +366,7 @@ const ViewByBomDetails: React.FC<Props> = ({ apiRef, isEdit, type, dayPeriod }) | |||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| {`${t("Material Demand Date")}: ${date}`} | |||
| </Typography> | |||
| <ScheduleTable<ProdScheduleLineResultByBomByDate> | |||
| <ScheduleTable<RoughProdScheduleLineResultByBomByDate> | |||
| // index={index} | |||
| type={type} | |||
| items={getValues("prodScheduleLinesByBomByDate")[index + 1]} // Use the corresponding records for the day | |||
| @@ -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<GridApiCommunity> | |||
| @@ -50,7 +50,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, dayPeriod }) = | |||
| control, | |||
| getValues, | |||
| formState: { errors, defaultValues, touchedFields }, | |||
| } = useFormContext<ProdScheduleResult>(); | |||
| } = useFormContext<RoughProdScheduleResult>(); | |||
| const { fields } = useFieldArray({ | |||
| control, | |||
| @@ -125,7 +125,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, dayPeriod }) = | |||
| }); | |||
| }; | |||
| const columns = useMemo<Column<ProdScheduleLineResultByFg>[]>( | |||
| const columns = useMemo<Column<RoughProdScheduleLineResultByFg>[]>( | |||
| () => [ | |||
| { | |||
| field: "code", | |||
| @@ -177,7 +177,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, dayPeriod }) = | |||
| [] | |||
| ); | |||
| const overallColumns = useMemo<Column<ProdScheduleLineResultByFg>[]>( | |||
| const overallColumns = useMemo<Column<RoughProdScheduleLineResultByFg>[]>( | |||
| () => [ | |||
| { | |||
| field: "code", | |||
| @@ -270,7 +270,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, dayPeriod }) = | |||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| {t("FG Demand List (7 Days)")} | |||
| </Typography> | |||
| <ScheduleTable<ProdScheduleLineResultByFg> | |||
| <ScheduleTable<RoughProdScheduleLineResultByFg> | |||
| // index={7} | |||
| type={type} | |||
| items={getValues("prodScheduleLinesByFg")} | |||
| @@ -288,9 +288,9 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, dayPeriod }) = | |||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||
| {`${t("FG Demand Date")}: ${date}`} | |||
| </Typography> | |||
| <ScheduleTable<ProdScheduleLineResultByFg> | |||
| <ScheduleTable<RoughProdScheduleLineResultByFg> | |||
| 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]} | |||
| @@ -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 | |||
| } | |||
| @@ -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<T extends ResultWithId>({ | |||
| 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<T extends ResultWithId>({ | |||
| return ( | |||
| <> | |||
| <TableRow hover tabIndex={-1} key={row.id}> | |||
| {isDetailType(type) && <TableCell> | |||
| {isDetailedType(type) && <TableCell> | |||
| <IconButton disabled={!isEdit}> | |||
| <PlayCircleOutlineIcon /> | |||
| </IconButton> | |||
| @@ -167,12 +167,12 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| {(editingRowId === row.id) ? ( | |||
| <> | |||
| { | |||
| isDetailType(type) && isEditable && <IconButton disabled={!isEdit} onClick={() => handleSaveClick(row)}> | |||
| isDetailedType(type) && isEditable && <IconButton disabled={!isEdit} onClick={() => handleSaveClick(row)}> | |||
| <SaveIcon /> | |||
| </IconButton> | |||
| } | |||
| { | |||
| isDetailType(type) && isEditable && <IconButton disabled={!isEdit} onClick={() => setEditingRowId(null)}> | |||
| isDetailedType(type) && isEditable && <IconButton disabled={!isEdit} onClick={() => setEditingRowId(null)}> | |||
| <CancelIcon /> | |||
| </IconButton> | |||
| } | |||
| @@ -190,13 +190,13 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| ) : ( | |||
| <> | |||
| { | |||
| isDetailType(type) && isEditable && <IconButton disabled={!isEdit} | |||
| isDetailedType(type) && isEditable && <IconButton disabled={!isEdit} | |||
| onClick={() => handleEditClick(row.id as number)}> | |||
| <EditIcon /> | |||
| </IconButton> | |||
| } | |||
| { | |||
| isDetailType(type) && isEditable && <IconButton disabled={!isEdit} | |||
| isDetailedType(type) && isEditable && <IconButton disabled={!isEdit} | |||
| onClick={() => handleDeleteClick(row.id as number)}> | |||
| <DeleteIcon /> | |||
| </IconButton> | |||
| @@ -278,7 +278,7 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| <TableCell> | |||
| <BomMaterialTable | |||
| type={type} | |||
| bomMaterial={(row as unknown as ProdScheduleLineResultByFg).bomMaterials} | |||
| bomMaterial={(row as unknown as RoughProdScheduleLineResultByFg).bomMaterials} | |||
| /> | |||
| </TableCell> | |||
| </TableRow> | |||
| @@ -298,7 +298,7 @@ function ScheduleTable<T extends ResultWithId>({ | |||
| <Table stickyHeader> | |||
| <TableHead> | |||
| <TableRow> | |||
| {isDetailType(type) && <TableCell>{t("Release")}</TableCell>} | |||
| {isDetailedType(type) && <TableCell>{t("Release")}</TableCell>} | |||
| {(isEditable || hasCollapse) && <TableCell>{t("Actions")}</TableCell>} {/* Action Column Header */} | |||
| {columns.map((column, idx) => ( | |||
| <TableCell style={column.style} key={`${column.field.toString()}${idx}`}> | |||
| @@ -196,7 +196,7 @@ function SearchBox<T extends string>({ | |||
| <Grid key={c.paramName} item xs={6}> | |||
| {c.type === "text" && ( | |||
| <TextField | |||
| label={c.label} | |||
| label={t(c.label)} | |||
| fullWidth | |||
| onChange={makeInputChangeHandler(c.paramName)} | |||
| value={inputs[c.paramName]} | |||
| @@ -204,7 +204,7 @@ function SearchBox<T extends string>({ | |||
| )} | |||
| {c.type === "multi-select" && ( | |||
| <MultiSelect | |||
| label={c.label} | |||
| label={t(c.label)} | |||
| options={c?.options} | |||
| selectedValues={c.filterObj?.[c.paramName] ?? []} | |||
| onChange={c.handleSelectionChange} | |||
| @@ -213,9 +213,9 @@ function SearchBox<T extends string>({ | |||
| )} | |||
| {c.type === "select" && ( | |||
| <FormControl fullWidth> | |||
| <InputLabel>{c.label}</InputLabel> | |||
| <InputLabel>{t(c.label)}</InputLabel> | |||
| <Select | |||
| label={c.label} | |||
| label={t(c.label)} | |||
| onChange={makeSelectChangeHandler(c.paramName)} | |||
| value={inputs[c.paramName]} | |||
| > | |||
| @@ -230,9 +230,9 @@ function SearchBox<T extends string>({ | |||
| )} | |||
| {c.type === "select-labelled" && ( | |||
| <FormControl fullWidth> | |||
| <InputLabel>{c.label}</InputLabel> | |||
| <InputLabel>{t(c.label)}</InputLabel> | |||
| <Select | |||
| label={c.label} | |||
| label={t(c.label)} | |||
| onChange={makeSelectChangeHandler(c.paramName)} | |||
| value={inputs[c.paramName]} | |||
| > | |||
| @@ -312,7 +312,7 @@ function SearchBox<T extends string>({ | |||
| </MenuItem> | |||
| ); | |||
| }} | |||
| renderInput={(params) => <TextField {...params} variant="outlined" label={c.label} />} | |||
| renderInput={(params) => <TextField {...params} variant="outlined" label={t(c.label)} />} | |||
| /> | |||
| )} | |||
| {c.type === "dateRange" && ( | |||
| @@ -324,7 +324,7 @@ function SearchBox<T extends string>({ | |||
| <Box display="flex"> | |||
| <FormControl fullWidth> | |||
| <DatePicker | |||
| label={c.label} | |||
| label={t(c.label)} | |||
| onChange={makeDateChangeHandler(c.paramName)} | |||
| value={dayjs(inputs[c.paramName]).isValid() ? dayjs(inputs[c.paramName]) : null} | |||
| /> | |||
| @@ -339,7 +339,7 @@ function SearchBox<T extends string>({ | |||
| </Box> | |||
| <FormControl fullWidth> | |||
| <DatePicker | |||
| label={c.label2} | |||
| label={c.label2 ? t(c.label2) : null} | |||
| onChange={makeDateToChangeHandler(c.paramName)} | |||
| value={dayjs(inputs[`${c.paramName}To`]).isValid() ? dayjs(inputs[`${c.paramName}To`]) : null} | |||
| /> | |||
| @@ -356,7 +356,7 @@ function SearchBox<T extends string>({ | |||
| <Box display="flex"> | |||
| <FormControl fullWidth> | |||
| <DatePicker | |||
| label={c.label} | |||
| label={t(c.label)} | |||
| onChange={makeDateChangeHandler(c.paramName)} | |||
| /> | |||
| </FormControl> | |||
| @@ -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<T extends ResultWithId> { | |||
| label: string; | |||
| type: string; | |||
| options?: T[]; | |||
| renderCell?: (T) => void; | |||
| style?: Partial<HTMLElement["style"]> & { [propName: string]: string }; | |||
| renderCell?: (params: T) => React.ReactNode; | |||
| style?: Partial<HTMLElement["style"]> & { [propName: string]: string } & CSSProperties; | |||
| // style?: Partial<HTMLElement["style"]> & { [propName: string]: string }; | |||
| } | |||
| interface ColumnWithAction<T extends ResultWithId> extends BaseColumn<T> { | |||
| @@ -52,7 +54,11 @@ interface Props<T extends ResultWithId> { | |||
| 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<T extends ResultWithId>({ | |||
| @@ -78,7 +84,7 @@ function EditableSearchResults<T extends ResultWithId>({ | |||
| }, [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<HTMLInputElement>) => { | |||
| @@ -134,7 +140,7 @@ function EditableSearchResults<T extends ResultWithId>({ | |||
| <> | |||
| <TableRow hover tabIndex={-1} key={row.id}> | |||
| <TableCell> | |||
| <IconButton disable={!isEdit}> | |||
| <IconButton disabled={!isEdit}> | |||
| <PlayCircleOutlineIcon /> | |||
| </IconButton> | |||
| </TableCell> | |||
| @@ -208,15 +214,15 @@ function EditableSearchResults<T extends ResultWithId>({ | |||
| onChange={(e) => handleInputChange(row.id as number, columnName, e.target.value)} | |||
| /> | |||
| ); | |||
| case 'multi-select': | |||
| return ( | |||
| <MultiSelect | |||
| //label={column.label} | |||
| options={column.options} | |||
| selectedValues={[]} | |||
| onChange={(selectedValues) => handleInputChange(row.id as number, columnName, selectedValues)} | |||
| /> | |||
| ); | |||
| // case 'multi-select': | |||
| // return ( | |||
| // <MultiSelect | |||
| // //label={column.label} | |||
| // options={column.options} | |||
| // selectedValues={[]} | |||
| // onChange={(selectedValues) => handleInputChange(row.id as number, columnName, selectedValues)} | |||
| // /> | |||
| // ); | |||
| case 'read-only': | |||
| return ( | |||
| <span> | |||
| @@ -253,7 +259,7 @@ function EditableSearchResults<T extends ResultWithId>({ | |||
| <TableRow> | |||
| <TableCell> | |||
| <TempInputGridForMockUp | |||
| stockInLine={row.lines as any[]} | |||
| stockInLine={(row as unknown as {lines: string[]}).lines as any[]} | |||
| /> | |||
| </TableCell> | |||
| </TableRow> | |||
| @@ -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": "總預估需求量", | |||