@@ -0,0 +1,48 @@ | |||
import { TypeEnum } from "@/app/utils/typeEnum"; | |||
import ItemsSearch from "@/components/ItemsSearch"; | |||
import { getServerI18n } from "@/i18n"; | |||
import Add from "@mui/icons-material/Add"; | |||
import Button from "@mui/material/Button"; | |||
import Stack from "@mui/material/Stack"; | |||
import Typography from "@mui/material/Typography"; | |||
import { Metadata } from "next"; | |||
import Link from "next/link"; | |||
import { Suspense } from "react"; | |||
export const metadata: Metadata = { | |||
title: "Detail Scheduling", | |||
}; | |||
const detailScheduling: React.FC = async () => { | |||
const project = TypeEnum.PRODUCT | |||
const { t } = await getServerI18n(project); | |||
// preloadClaims(); | |||
return ( | |||
<> | |||
<Stack | |||
direction="row" | |||
justifyContent="space-between" | |||
flexWrap="wrap" | |||
rowGap={2} | |||
> | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Detail Scheduling")} | |||
</Typography> | |||
{/* <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="product/create" | |||
> | |||
{t("Create product")} | |||
</Button> */} | |||
</Stack> | |||
<Suspense fallback={<ItemsSearch.Loading />}> | |||
<ItemsSearch /> | |||
</Suspense> | |||
</> | |||
); | |||
}; | |||
export default detailScheduling; |
@@ -0,0 +1,11 @@ | |||
import { Metadata } from "next"; | |||
export const metadata: Metadata = { | |||
title: "Scheduling", | |||
}; | |||
const Scheduling: React.FC = async () => { | |||
return null; | |||
}; | |||
export default Scheduling; |
@@ -0,0 +1,48 @@ | |||
import { TypeEnum } from "@/app/utils/typeEnum"; | |||
import RoughSchedule from "@/components/RoughSchedule"; | |||
import { getServerI18n } from "@/i18n"; | |||
import Add from "@mui/icons-material/Add"; | |||
import Button from "@mui/material/Button"; | |||
import Stack from "@mui/material/Stack"; | |||
import Typography from "@mui/material/Typography"; | |||
import { Metadata } from "next"; | |||
import Link from "next/link"; | |||
import { Suspense } from "react"; | |||
export const metadata: Metadata = { | |||
title: "Rough Scheduling", | |||
}; | |||
const roughScheduling: React.FC = async () => { | |||
const project = TypeEnum.PRODUCT | |||
const { t } = await getServerI18n(project); | |||
// preloadClaims(); | |||
return ( | |||
<> | |||
<Stack | |||
direction="row" | |||
justifyContent="space-between" | |||
flexWrap="wrap" | |||
rowGap={2} | |||
> | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Rough Scheduling")} | |||
</Typography> | |||
{/* <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="product/create" | |||
> | |||
{t("Create product")} | |||
</Button> */} | |||
</Stack> | |||
<Suspense fallback={<RoughSchedule.Loading />}> | |||
<RoughSchedule /> | |||
</Suspense> | |||
</> | |||
); | |||
}; | |||
export default roughScheduling; |
@@ -14,6 +14,8 @@ const pathToLabelMap: { [path: string]: string } = { | |||
"/tasks/create": "Create Task Template", | |||
"/settings/qcItem": "Qc Item", | |||
"/settings/rss": "Rough Schedule Setting", | |||
"/scheduling/rough": "Rough Scheduling", | |||
"/scheduling/detail": "Detail Scheduling", | |||
}; | |||
const Breadcrumb = () => { | |||
@@ -166,6 +166,23 @@ const NavigationContent: React.FC = () => { | |||
}, | |||
], | |||
}, | |||
{ | |||
icon: <RequestQuote />, | |||
label: "Scheduling", | |||
path: "", | |||
children: [ | |||
{ | |||
icon: <RequestQuote />, | |||
label: "Rough Scheduling", | |||
path: "/scheduling/rough", | |||
}, | |||
{ | |||
icon: <RequestQuote />, | |||
label: "Detail Scheduling", | |||
path: "/scheduling/detail", | |||
}, | |||
], | |||
}, | |||
{ | |||
icon: <RequestQuote />, | |||
label: "Settings", | |||
@@ -0,0 +1,194 @@ | |||
"use client"; | |||
import React, {useCallback, useEffect, useMemo, useState} from "react"; | |||
import SearchBox, { Criterion } from "../SearchBox"; | |||
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 { 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 | |||
type RecordStructure ={ | |||
id: number, | |||
schedulePeriod: string, | |||
scheduleAt: string | |||
}; | |||
type Props = { | |||
records: RecordStructure[]; | |||
}; | |||
type SearchQuery = Partial<Omit<ItemsResult, "id">>; | |||
type SearchParamNames = keyof SearchQuery; | |||
const RSOverview: React.FC<Props> = ({ records }) => { | |||
const [filteredItems, setFilteredItems] = useState<Object[]>(records ?? []); | |||
const { t } = useTranslation("items"); | |||
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 | |||
var title = ""; | |||
var mode = "Search"; | |||
var redirPath = ""; | |||
title = "Product"; | |||
redirPath = "/scheduling/rough"; | |||
return [mode, redirPath]; | |||
}, []); | |||
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( | |||
() => { | |||
var searchCriteria: Criterion<SearchParamNames>[] = [ | |||
{ label: t("Schedule Period"), paramName: "schedulePeriod", type: "dateRange" }, | |||
{ label: t("Scheduled At"), paramName: "scheduleAt", type: "date" }, | |||
{ label: t("Product Count"), paramName: "productCount", type: "input" }, | |||
] | |||
return searchCriteria | |||
}, | |||
[t, records] | |||
); | |||
// const onDetailClick = useCallback( | |||
// (item: ItemsResult) => { | |||
// router.push(`/settings/items/edit?id=${item.id}`); | |||
// }, | |||
// [router] | |||
// ); | |||
const onDeleteClick = useCallback( | |||
(item: ItemsResult) => {}, | |||
[router] | |||
); | |||
const columns = useMemo<Column<ItemsResult>[]>( | |||
() => [ | |||
{ | |||
name: "id", | |||
label: t("Details"), | |||
onClick: ()=>{}, | |||
buttonIcon: <EditNote />, | |||
}, | |||
{ | |||
name: "scheduledPeriod", | |||
label: "Rough Schedule Period", | |||
}, | |||
{ | |||
name: "scheduledAt", | |||
label: t("Scheduled At"), | |||
}, | |||
{ | |||
name: "productCount", | |||
label: t("Product Count(s)"), | |||
}, | |||
// { | |||
// name: "action", | |||
// label: t(""), | |||
// buttonIcon: <GridDeleteIcon />, | |||
// onClick: onDeleteClick, | |||
// }, | |||
], | |||
[filteredItems] | |||
); | |||
useEffect(() => { | |||
refetchData(filterObj); | |||
}, [filterObj, pagingController.pageNum, pagingController.pageSize]); | |||
const refetchData = async (filterObj: SearchQuery) => { | |||
const authHeader = axiosInstance.defaults.headers['Authorization']; | |||
if (!authHeader) { | |||
return; // Exit the function if the token is not set | |||
} | |||
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(); | |||
}, [records]); | |||
return ( | |||
<> | |||
<SearchBox | |||
criteria={searchCriteria} | |||
onSearch={(query) => { | |||
setFilterObj({ | |||
...query | |||
}) | |||
}} | |||
onReset={onReset} | |||
/> | |||
<SearchResults<ItemsResult> | |||
items={filteredItems} | |||
columns={columns} | |||
setPagingController={setPagingController} | |||
pagingController={pagingController} | |||
isAutoPaging={false} | |||
/> | |||
</> | |||
); | |||
}; | |||
export default RSOverview; |
@@ -0,0 +1,40 @@ | |||
import Card from "@mui/material/Card"; | |||
import CardContent from "@mui/material/CardContent"; | |||
import Skeleton from "@mui/material/Skeleton"; | |||
import Stack from "@mui/material/Stack"; | |||
import React from "react"; | |||
// Can make this nicer | |||
export const RoughScheduleLoading: React.FC = () => { | |||
return ( | |||
<> | |||
<Card> | |||
<CardContent> | |||
<Stack spacing={2}> | |||
<Skeleton variant="rounded" height={60} /> | |||
<Skeleton variant="rounded" height={60} /> | |||
<Skeleton variant="rounded" height={60} /> | |||
<Skeleton | |||
variant="rounded" | |||
height={50} | |||
width={100} | |||
sx={{ alignSelf: "flex-end" }} | |||
/> | |||
</Stack> | |||
</CardContent> | |||
</Card> | |||
<Card> | |||
<CardContent> | |||
<Stack spacing={2}> | |||
<Skeleton variant="rounded" height={40} /> | |||
<Skeleton variant="rounded" height={40} /> | |||
<Skeleton variant="rounded" height={40} /> | |||
<Skeleton variant="rounded" height={40} /> | |||
</Stack> | |||
</CardContent> | |||
</Card> | |||
</> | |||
); | |||
}; | |||
export default RoughScheduleLoading; |
@@ -0,0 +1,19 @@ | |||
import { fetchAllItems, } from "@/app/api/settings/item"; | |||
import {RoughScheduleLoading} from "./RoughScheduleLoading"; | |||
import RSOverview from "./RoughSchedileSearchView"; | |||
interface SubComponents { | |||
Loading: typeof RoughScheduleLoading; | |||
} | |||
const RoughScheduleWrapper: ({}: {}) => Promise<JSX.Element> = async ({ | |||
// type, | |||
}) => { | |||
// console.log(type) | |||
var result = await fetchAllItems() | |||
return <RSOverview records={result} />; | |||
}; | |||
RoughScheduleWrapper.Loading = RoughScheduleLoading; | |||
export default RoughScheduleWrapper; |
@@ -0,0 +1 @@ | |||
export { default } from "./RoughScheduleWrapper"; |
@@ -196,6 +196,22 @@ function SearchBox<T extends string>({ | |||
</Box> | |||
</LocalizationProvider> | |||
)} | |||
{c.type === "date" && ( | |||
<LocalizationProvider | |||
dateAdapter={AdapterDayjs} | |||
// TODO: Should maybe use a custom adapterLocale here to support YYYY-MM-DD | |||
adapterLocale="zh-hk" | |||
> | |||
<Box display="flex"> | |||
<FormControl fullWidth> | |||
<DatePicker | |||
label={c.label} | |||
onChange={makeDateChangeHandler(c.paramName)} | |||
/> | |||
</FormControl> | |||
</Box> | |||
</LocalizationProvider> | |||
)} | |||
</Grid> | |||
); | |||
})} | |||