diff --git a/package.json b/package.json
index 7780a2b..a8e487c 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"@mui/x-date-pickers": "^6.18.7",
"@unly/universal-language-detector": "^2.0.3",
"apexcharts": "^3.45.2",
+ "axios": "^1.9.0",
"dayjs": "^1.11.10",
"i18next": "^23.7.11",
"i18next-resources-to-backend": "^1.2.0",
@@ -28,6 +29,7 @@
"next": "14.0.4",
"next-auth": "^4.24.5",
"next-pwa": "^5.6.0",
+ "qs": "^6.14.0",
"react": "^18",
"react-apexcharts": "^1.4.1",
"react-dom": "^18",
diff --git a/src/app/(main)/settings/rss/page.tsx b/src/app/(main)/settings/rss/page.tsx
index 2bf97dd..d4af0d4 100644
--- a/src/app/(main)/settings/rss/page.tsx
+++ b/src/app/(main)/settings/rss/page.tsx
@@ -8,6 +8,8 @@ import Typography from "@mui/material/Typography";
import { Metadata } from "next";
import Link from "next/link";
import { Suspense } from "react";
+import RoughScheduleLoading from "@/components/RoughScheduleSetting/RoughScheduleLoading";
+import RoughScheduleSetting from "@/components/RoughScheduleSetting/RoughScheduleSetting";
export const metadata: Metadata = {
title: "Rough Schedule Setting",
@@ -38,8 +40,8 @@ const roughScheduleSetting: React.FC = async () => {
{t("Create product")}
*/}
- }>
-
+ }>
+
>
);
diff --git a/src/components/RoughScheduleSetting/RoughScheduleLoading.tsx b/src/components/RoughScheduleSetting/RoughScheduleLoading.tsx
new file mode 100644
index 0000000..62db673
--- /dev/null
+++ b/src/components/RoughScheduleSetting/RoughScheduleLoading.tsx
@@ -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 (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default RoughScheduleLoading;
diff --git a/src/components/RoughScheduleSetting/RoughScheduleSetting.tsx b/src/components/RoughScheduleSetting/RoughScheduleSetting.tsx
index 2416dca..c42b687 100644
--- a/src/components/RoughScheduleSetting/RoughScheduleSetting.tsx
+++ b/src/components/RoughScheduleSetting/RoughScheduleSetting.tsx
@@ -1,191 +1,185 @@
"use client";
-import { 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 SearchResults, { Column } from "../SearchResults";
+import { EditNote } from "@mui/icons-material";
import { useRouter, useSearchParams } from "next/navigation";
-
-import {
- FormProvider,
- SubmitErrorHandler,
- SubmitHandler,
- useForm,
-} from "react-hook-form";
-import { deleteDialog } from "../Swal/CustomAlerts";
-import { Box, Button, Grid, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material";
-import { Check, Close, EditNote } from "@mui/icons-material";
-import { useGridApiRef } from "@mui/x-data-grid";
-import {CreateItemInputs, saveItem} from "@/app/api/settings/item/actions";
-import {saveItemQcChecks} from "@/app/api/settings/qcCheck/actions";
-import {useTranslation} from "react-i18next/index";
-import {ItemQc} from "@/app/api/settings/item";
+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'; // Make sure to import Qs
type Props = {
- isEditMode: boolean;
- // type: TypeEnum;
- defaultValues: Partial | undefined;
- qcChecks: ItemQc[]
+ items: ItemsResult[];
};
+type SearchQuery = Partial>;
+type SearchParamNames = keyof SearchQuery;
-const CreateItem: React.FC = ({
- isEditMode,
- // type,
- defaultValues,
- qcChecks
- }) => {
- // 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();
+const RSSOverview: React.FC = ({ items }) => {
+ const [filteredItems, setFilteredItems] = useState(items ?? []);
+ const { t } = useTranslation("items");
const router = useRouter();
- const title = "Product / Material"
+ 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 = "";
+ var mode = "Search";
var redirPath = "";
- // if (type === TypeEnum.MATERIAL) {
- // typeId = TypeEnum.MATERIAL_ID
- // title = "Material";
- // redirPath = "/settings/material";
- // }
- // if (type === TypeEnum.PRODUCT) {
- // typeId = TypeEnum.PRODUCT_ID
- title = "Rough Schedule";
+ title = "Product";
redirPath = "/settings/rss";
- // }
- // if (type === TypeEnum.BYPRODUCT) {
- // typeId = TypeEnum.BYPRODUCT_ID
- // title = "By-Product";
- // redirPath = "/settings/byProduct";
- // }
- if (isEditMode) {
- mode = "Edit";
- } else {
- mode = "Create";
- }
return [mode, redirPath];
- }, [isEditMode]);
- // console.log(typeId)
- const formProps = useForm({
- defaultValues: defaultValues ? defaultValues : {
- },
- });
- const errors = formProps.formState.errors;
+ }, []);
- const handleCancel = () => {
- router.replace(`/settings/product`);
+ const handleSelectionChange = (selectedValues: string[]) => {
+ setTempSelectedValue({
+ ...tempSelectedValue,
+ excludeDate: selectedValues,
+ });
};
- const onSubmit = useCallback>(
- async (data, event) => {
- let hasErrors = false;
- console.log(errors)
- // console.log(apiRef.current.getCellValue(2, "lowerLimit"))
- // apiRef.current.
- try {
- if (hasErrors) {
- setServerError(t("An error has occurred. Please try again later.") as String);
- return false;
- }
- console.log("data posted");
- console.log(data);
- const qcCheck = data.qcChecks.length > 0 ? data.qcChecks.filter((q) => data.qcChecks_active.includes(q.id!!)).map((qc) => {
- return {
- qcItemId: qc.id,
- instruction: qc.instruction,
- lowerLimit: qc.lowerLimit,
- upperLimit: qc.upperLimit,
- itemId: parseInt(params.get("id")!.toString())
- }
- }) : []
-
- const test = data.qcChecks.filter((q) => data.qcChecks_active.includes(q.id!!))
- // TODO:
- // 1. check field ( directly modify col def / check here )
- // 2. set error change tab index
- console.log(test)
- console.log(qcCheck)
- // return
- // do api
- console.log("asdad")
- var responseI = await saveItem(data);
- console.log("asdad")
- var responseQ = await saveItemQcChecks(qcCheck)
- if (responseI && responseQ) {
- if (!Boolean(responseI.id)) {
- formProps.setError(responseI.errorPosition!! as keyof CreateItemInputs, {
- message: responseI.message!!,
- type: "required",
- });
- } else if (!Boolean(responseQ.id)) {
-
- } else if (Boolean(responseI.id) && Boolean(responseQ.id)) {
- router.replace(redirPath);
- }
- }
- } catch (e) {
- // backend error
- setServerError(t("An error has occurred. Please try again later."));
- console.log(e);
- }
+
+ const dayOptions = [
+ {label: "Mon", value: 1},
+ {label: "Tue", value: 2},
+ {label: "Wed", value: 3},
+ {label: "Thu", value: 4},
+ {label: "Fri", value: 5},
+ {label: "Sat", value: 6},
+ {label: "Sun", value: 7},
+ ];
+
+ const searchCriteria: Criterion[] = useMemo(
+ () => {
+ var searchCriteria: Criterion[] = [
+ { label: t("Finished Goods Name"), paramName: "fgName", type: "text" },
+ {
+ label: t("Exclude Date"),
+ paramName: "excludeDate",
+ type: "multi-select",
+ options: dayOptions,
+ selectedValues: filterObj,
+ handleSelectionChange: handleSelectionChange,
+ },
+ ]
+ return searchCriteria
},
- [apiRef, router, t]
+ [t, items]
);
- // multiple tabs
- const onSubmitError = useCallback>(
- (errors) => {},
- []
+ const onDetailClick = useCallback(
+ (item: ItemsResult) => {
+ router.push(`/settings/items/edit?id=${item.id}`);
+ },
+ [router]
+ );
+
+ const onDeleteClick = useCallback(
+ (item: ItemsResult) => {},
+ [router]
);
+ const columns = useMemo[]>(
+ () => [
+ {
+ name: "id",
+ label: t("Details"),
+ onClick: onDetailClick,
+ buttonIcon: ,
+ },
+ {
+ name: "fgName",
+ label: "Finished Goods Name",
+ },
+ {
+ name: "excludeDate",
+ label: t("Exclude Date"),
+ },
+ {
+ name: "action",
+ label: t(""),
+ buttonIcon: ,
+ 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(`${NEXT_PUBLIC_API_URL}/items/getRecordByPage`, {
+ params,
+ paramsSerializer: (params) => {
+ return Qs.stringify(params, { arrayFormat: 'repeat' });
+ },
+ });
+ setFilteredItems(response.data.records);
+ 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();
+ }, [items]);
+
return (
<>
-
-
-
-
- {t(`${mode} ${title}`)}
-
-
-
-
-
-
- {serverError && (
-
- {serverError}
-
- )}
- {tabIndex === 0 && }
- {tabIndex === 1 && }
- {/* {type === TypeEnum.MATERIAL && } */}
- {/* {type === TypeEnum.BYPRODUCT && } */}
-
- }
- type="submit"
- // disabled={submitDisabled}
- >
- {isEditMode ? t("Save") as String : t("Confirm") as String}
-
- }
- onClick={handleCancel}
- >
- {t("Cancel") as String}
-
-
-
-
+ {
+ setFilterObj({
+ ...query
+ })
+ }}
+ onReset={onReset}
+ />
+
+ items={filteredItems}
+ columns={columns}
+ setPagingController={setPagingController}
+ pagingController={pagingController}
+ isAutoPaging={false}
+ />
>
);
};
-export default CreateItem;
+
+export default RSSOverview;
diff --git a/src/components/RoughScheduleSetting/RoughScheduleSettingWrapper.tsx b/src/components/RoughScheduleSetting/RoughScheduleSettingWrapper.tsx
index 93a7bd6..2dd5145 100644
--- a/src/components/RoughScheduleSetting/RoughScheduleSettingWrapper.tsx
+++ b/src/components/RoughScheduleSetting/RoughScheduleSettingWrapper.tsx
@@ -1,51 +1,23 @@
-import {CreateItemInputs} from "@/app/api/settings/item/actions";
-import CreateItemLoading from "../CreateItem/CreateItemLoading";
-import {fetchItem} from "@/app/api/settings/item";
-import CreateItem from "@/components/RoughScheduleSetting/RoughScheduleSetting";
+import { fetchAllItems, } from "@/app/api/settings/item";
+import {RoughScheduleLoading} from "./RoughScheduleLoading";
+import RSSOverview from "@/components/RoughScheduleSetting/RoughScheduleSetting";
interface SubComponents {
- Loading: typeof CreateItemLoading;
+ Loading: typeof RoughScheduleLoading;
}
type Props = {
- id?: number
// type: TypeEnum;
};
-const RoughScheduleSettingWrapper: ({id}: { id: any }) => Promise = async ({ id }) => {
- var result
- var defaultValues: Partial | undefined
+const RoughScheduleSettingWrapper: ({}: {}) => Promise = async ({
+ // type,
+ }) => {
// console.log(type)
- var qcChecks
- if (id) {
- result = await fetchItem(id);
- const item = result.item
- qcChecks = result.qcChecks
- const activeRows = qcChecks.filter(it => it.isActive).map(i => i.id)
- console.log(qcChecks)
- defaultValues = {
- type: item?.type,
- id: item?.id,
- code: item?.code,
- name: item?.name,
- description: item?.description,
- remarks: item?.remarks,
- shelfLife: item?.shelfLife,
- countryOfOrigin: item?.countryOfOrigin,
- maxQty: item?.maxQty,
- qcChecks: qcChecks,
- qcChecks_active: activeRows
- };
- }
-
- return (
-
- );
+ var result = await fetchAllItems()
+ return ;
};
-RoughScheduleSettingWrapper.Loading = CreateItemLoading;
+
+RoughScheduleSettingWrapper.Loading = RoughScheduleLoading;
export default RoughScheduleSettingWrapper;
diff --git a/src/components/SearchBox/MultiSelect.tsx b/src/components/SearchBox/MultiSelect.tsx
new file mode 100644
index 0000000..ba71493
--- /dev/null
+++ b/src/components/SearchBox/MultiSelect.tsx
@@ -0,0 +1,67 @@
+import React, {useEffect, useState} from 'react';
+import { FormControl, InputLabel, Select, MenuItem, Chip, Box } from '@mui/material';
+
+interface Option {
+ value: number;
+ label: string;
+}
+
+interface MultiSelectProps {
+ label: string;
+ options: Option[];
+ selectedValues: number[];
+ onChange: (values: number[]) => void;
+}
+
+const MultiSelect: React.FC = ({ label, options, selectedValues, onChange, isReset }) => {
+ const [displayValues, setDisplayValues] = useState(selectedValues);
+
+ const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
+ const value = event.target.value as number[];
+ console.log("[debug] value", value);
+
+ // Update display values state
+ setDisplayValues(value);
+ // Update selected values in parent component
+ onChange(value);
+ };
+
+ useEffect(()=>{
+ setDisplayValues([]);
+ }, [isReset])
+
+ return (
+
+ {label}
+
+
+ );
+};
+
+export default MultiSelect;
\ No newline at end of file
diff --git a/src/components/SearchBox/SearchBox.tsx b/src/components/SearchBox/SearchBox.tsx
index 56c9b13..b1a5512 100644
--- a/src/components/SearchBox/SearchBox.tsx
+++ b/src/components/SearchBox/SearchBox.tsx
@@ -21,12 +21,16 @@ import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { Box } from "@mui/material";
+import MultiSelect from "@/components/SearchBox/MultiSelect";
interface BaseCriterion {
label: string;
label2?: string;
paramName: T;
paramName2?: T;
+ options?: T[];
+ filterObj?: T;
+ handleSelectionChange: (selectedOptions: T[]) => void;
}
interface TextCriterion extends BaseCriterion {
@@ -35,7 +39,14 @@ interface TextCriterion extends BaseCriterion {
interface SelectCriterion extends BaseCriterion {
type: "select";
- options: string[];
+ options: T[];
+}
+
+interface MultiSelectCriterion extends BaseCriterion {
+ type: "select";
+ options: T[];
+ selectedOptions: T[];
+ handleSelectionChange: (selectedOptions: T[]) => void;
}
interface DateRangeCriterion extends BaseCriterion {
@@ -45,7 +56,8 @@ interface DateRangeCriterion extends BaseCriterion {
export type Criterion =
| TextCriterion
| SelectCriterion
- | DateRangeCriterion;
+ | DateRangeCriterion
+ | MultiSelectCriterion;
interface Props {
criteria: Criterion[];
@@ -70,6 +82,7 @@ function SearchBox({
[criteria],
);
const [inputs, setInputs] = useState(defaultInputs);
+ const [isReset, setIsReset] = useState(false);
const makeInputChangeHandler = useCallback(
(paramName: T): React.ChangeEventHandler => {
@@ -104,6 +117,7 @@ function SearchBox({
const handleReset = () => {
setInputs(defaultInputs);
onReset?.();
+ setIsReset(!isReset);
};
const handleSearch = () => {
@@ -126,6 +140,15 @@ function SearchBox({
value={inputs[c.paramName]}
/>
)}
+ {c.type === "multi-select" && (
+
+ )}
{c.type === "select" && (
{c.label}