# 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": "總預估需求量", | |||