|
|
@@ -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} |
|
|
|
/> |
|
|
|
</> |
|
|
|
); |
|
|
|