| @@ -0,0 +1,22 @@ | |||||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import CreateEquipmentType from "@/components/CreateEquipment"; | |||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | |||||
| import { Typography } from "@mui/material"; | |||||
| import isString from "lodash/isString"; | |||||
| type Props = {} & SearchParams; | |||||
| const materialSetting: React.FC<Props> = async ({ searchParams }) => { | |||||
| // const type = TypeEnum.PRODUCT; | |||||
| const { t } = await getServerI18n("common"); | |||||
| return ( | |||||
| <> | |||||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||||
| <I18nProvider namespaces={["common"]}> | |||||
| <CreateEquipmentType /> | |||||
| </I18nProvider> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default materialSetting; | |||||
| @@ -0,0 +1,29 @@ | |||||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import CreateEquipmentType from "@/components/CreateEquipment"; | |||||
| import { I18nProvider, getServerI18n } from "@/i18n"; | |||||
| import { Typography } from "@mui/material"; | |||||
| import isString from "lodash/isString"; | |||||
| import { notFound } from "next/navigation"; | |||||
| type Props = {} & SearchParams; | |||||
| const productSetting: React.FC<Props> = async ({ searchParams }) => { | |||||
| const type = "common"; | |||||
| const { t } = await getServerI18n(type); | |||||
| const id = isString(searchParams["id"]) | |||||
| ? parseInt(searchParams["id"]) | |||||
| : undefined; | |||||
| if (!id) { | |||||
| notFound(); | |||||
| } | |||||
| return ( | |||||
| <> | |||||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | |||||
| <I18nProvider namespaces={[type]}> | |||||
| <CreateEquipmentType id={id} /> | |||||
| </I18nProvider> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default productSetting; | |||||
| @@ -0,0 +1,52 @@ | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import EquipmentSearch from "@/components/EquipmentSearch"; | |||||
| 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"; | |||||
| import { fetchAllEquipments } from "@/app/api/settings/equipment"; | |||||
| import { I18nProvider } from "@/i18n"; | |||||
| export const metadata: Metadata = { | |||||
| title: "Equipment Type", | |||||
| }; | |||||
| const productSetting: React.FC = async () => { | |||||
| const type = "common"; | |||||
| const { t } = await getServerI18n(type); | |||||
| const equipments = await fetchAllEquipments(); | |||||
| // preloadClaims(); | |||||
| return ( | |||||
| <> | |||||
| <Stack | |||||
| direction="row" | |||||
| justifyContent="space-between" | |||||
| flexWrap="wrap" | |||||
| rowGap={2} | |||||
| > | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| {t("Equipment")} | |||||
| </Typography> | |||||
| {/* <Button | |||||
| variant="contained" | |||||
| startIcon={<Add />} | |||||
| LinkComponent={Link} | |||||
| href="product/create" | |||||
| > | |||||
| {t("Create product")} | |||||
| </Button> */} | |||||
| </Stack> | |||||
| <Suspense fallback={<EquipmentSearch.Loading />}> | |||||
| <I18nProvider namespaces={["common","project"]}> | |||||
| <EquipmentSearch /> | |||||
| </I18nProvider> | |||||
| </Suspense> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default productSetting; | |||||
| @@ -0,0 +1,38 @@ | |||||
| "use server"; | |||||
| import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil"; | |||||
| import { revalidateTag } from "next/cache"; | |||||
| import { BASE_API_URL } from "@/config/api"; | |||||
| import { CreateEquipmentTypeResponse } from "../../utils"; | |||||
| // export type TypeInputs = { | |||||
| // id: number; | |||||
| // name: string | |||||
| // } | |||||
| // export type UomInputs = { | |||||
| // uom: string | |||||
| // } | |||||
| // export type WeightUnitInputs = { | |||||
| // weightUnit: string | |||||
| // conversion: number | |||||
| // } | |||||
| export type CreateEquipmentInputs = { | |||||
| id?: string | number | |||||
| code: string; | |||||
| name: string; | |||||
| description?: string | undefined; | |||||
| equipmentTypeId?: string | number | undefined; | |||||
| } | |||||
| export const saveEquipment = async (data: CreateEquipmentInputs) => { | |||||
| // try { | |||||
| const equipment = await serverFetchJson<CreateEquipmentTypeResponse<CreateEquipmentInputs>>(`${BASE_API_URL}/Equipment/save`, { | |||||
| method: "POST", | |||||
| body: JSON.stringify(data), | |||||
| headers: { "Content-Type": "application/json" }, | |||||
| }); | |||||
| revalidateTag("EquipmentType"); | |||||
| return equipment | |||||
| }; | |||||
| @@ -0,0 +1,34 @@ | |||||
| import { cache } from "react"; | |||||
| import "server-only"; | |||||
| // import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||||
| // import { BASE_API_URL } from "@/config/api"; | |||||
| import { serverFetchJson } from "../../../utils/fetchUtil"; | |||||
| import { BASE_API_URL } from "../../../../config/api"; | |||||
| export { default } from "../../../../components/CreateEquipment/CreateEquipment"; | |||||
| // import { TypeInputs, UomInputs, WeightUnitInputs } from "./actions"; | |||||
| export type EquipmentResult = { | |||||
| id: string | number | |||||
| code: string; | |||||
| name: string; | |||||
| description: string | undefined; | |||||
| equipmentTypeId: string | number | undefined; | |||||
| action?: any | |||||
| } | |||||
| export type Result = { | |||||
| equipment: EquipmentResult | |||||
| } | |||||
| export const fetchAllEquipments = cache(async () => { | |||||
| return serverFetchJson<EquipmentResult[]>(`${BASE_API_URL}/Equipment`, { | |||||
| next: { tags: ["equipments"] }, | |||||
| }); | |||||
| }); | |||||
| export const fetchEquipment = cache(async (id: number) => { | |||||
| return serverFetchJson<EquipmentResult>(`${BASE_API_URL}/Equipment/details/${id}`, { | |||||
| next: { tags: ["equipments"] }, | |||||
| }); | |||||
| }); | |||||
| @@ -12,7 +12,13 @@ export interface CreateEquipmentTypeResponse<T> { | |||||
| message: string | null; | message: string | null; | ||||
| errorPosition: string | keyof T; | errorPosition: string | keyof T; | ||||
| } | } | ||||
| export interface CreateEquipmentResponse<T> { | |||||
| id: number | null; | |||||
| name: string; | |||||
| code: string; | |||||
| message: string | null; | |||||
| errorPosition: string | keyof T; | |||||
| } | |||||
| export interface RecordsRes<T>{ | export interface RecordsRes<T>{ | ||||
| records: T | records: T | ||||
| total: number | total: number | ||||
| @@ -0,0 +1,193 @@ | |||||
| "use client"; | |||||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import { useRouter, useSearchParams } from "next/navigation"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { | |||||
| CreateEquipmentInputs, | |||||
| saveEquipment, | |||||
| } from "@/app/api/settings/equipment/actions"; | |||||
| 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 { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import EquipmentDetails from "./EquipmentDetails"; | |||||
| import { CreateItemResponse } from "@/app/api/utils"; | |||||
| import { ItemQc } from "@/app/api/settings/item"; | |||||
| import { saveItemQcChecks } from "@/app/api/settings/qcCheck/actions"; | |||||
| import { useGridApiRef } from "@mui/x-data-grid"; | |||||
| type Props = { | |||||
| isEditMode: boolean; | |||||
| // type: TypeEnum; | |||||
| defaultValues: Partial<CreateEquipmentInputs> | undefined; | |||||
| }; | |||||
| const CreateItem: React.FC<Props> = ({ | |||||
| isEditMode, | |||||
| // type, | |||||
| defaultValues, | |||||
| }) => { | |||||
| // 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("common"); | |||||
| const router = useRouter(); | |||||
| const title = "Equipment" | |||||
| const [mode, redirPath] = useMemo(() => { | |||||
| // var typeId = TypeEnum.CONSUMABLE_ID | |||||
| var title = ""; | |||||
| var mode = ""; | |||||
| var redirPath = ""; | |||||
| // if (type === TypeEnum.MATERIAL) { | |||||
| // typeId = TypeEnum.MATERIAL_ID | |||||
| // title = "Material"; | |||||
| // redirPath = "/settings/material"; | |||||
| // } | |||||
| // if (type === TypeEnum.PRODUCT) { | |||||
| // typeId = TypeEnum.PRODUCT_ID | |||||
| title = "Equipment"; | |||||
| redirPath = "/settings/equipment"; | |||||
| // } | |||||
| // 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<CreateEquipmentInputs>({ | |||||
| defaultValues: defaultValues ? defaultValues : { | |||||
| }, | |||||
| }); | |||||
| const errors = formProps.formState.errors; | |||||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
| (_e, newValue) => { | |||||
| setTabIndex(newValue); | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| const handleCancel = () => { | |||||
| router.replace(`/settings/equipment`); | |||||
| }; | |||||
| const onSubmit = useCallback<SubmitHandler<CreateEquipmentInputs & {}>>( | |||||
| 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.")); | |||||
| return false; | |||||
| } | |||||
| console.log("data posted"); | |||||
| console.log(data); | |||||
| // TODO: | |||||
| // 1. check field ( directly modify col def / check here ) | |||||
| // 2. set error change tab index | |||||
| // return | |||||
| // do api | |||||
| console.log("asdad") | |||||
| var responseI = await saveEquipment(data); | |||||
| console.log("asdad") | |||||
| // var responseQ = await saveItemQcChecks(qcCheck) | |||||
| if (responseI) { | |||||
| if (!Boolean(responseI.id)) { | |||||
| formProps.setError(responseI.errorPosition!! as keyof CreateEquipmentInputs, { | |||||
| message: responseI.message!!, | |||||
| type: "required", | |||||
| }) | |||||
| } else if (Boolean(responseI.id)) { | |||||
| router.replace(redirPath); | |||||
| } | |||||
| } | |||||
| } catch (e) { | |||||
| // backend error | |||||
| setServerError(t("An error has occurred. Please try again later.")); | |||||
| console.log(e); | |||||
| } | |||||
| }, | |||||
| [apiRef, router, t] | |||||
| ); | |||||
| // multiple tabs | |||||
| const onSubmitError = useCallback<SubmitErrorHandler<CreateEquipmentInputs>>( | |||||
| (errors) => {}, | |||||
| [] | |||||
| ); | |||||
| return ( | |||||
| <> | |||||
| <FormProvider {...formProps}> | |||||
| <Stack | |||||
| spacing={2} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | |||||
| > | |||||
| <Grid> | |||||
| <Typography mb={2} variant="h4"> | |||||
| {t(`${mode} ${title}`)} | |||||
| </Typography> | |||||
| </Grid> | |||||
| <Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable"> | |||||
| <Tab label={t("Equipment Details")} iconPosition="end"/> | |||||
| {/* <Tab label={t("Qc items")} iconPosition="end" /> */} | |||||
| </Tabs> | |||||
| {serverError && ( | |||||
| <Typography variant="body2" color="error" alignSelf="flex-end"> | |||||
| {serverError} | |||||
| </Typography> | |||||
| )} | |||||
| {tabIndex === 0 && <EquipmentDetails isEditMode={isEditMode} />} | |||||
| {/* {tabIndex === 1 && <QcDetails apiRef={apiRef} />} */} | |||||
| {/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */} | |||||
| {/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */} | |||||
| {/* | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| name="submit" | |||||
| variant="contained" | |||||
| startIcon={<Check />} | |||||
| type="submit" | |||||
| // disabled={submitDisabled} | |||||
| > | |||||
| {isEditMode ? t("Save") : t("Confirm")} | |||||
| </Button> | |||||
| <Button | |||||
| variant="outlined" | |||||
| startIcon={<Close />} | |||||
| onClick={handleCancel} | |||||
| > | |||||
| {t("Cancel")} | |||||
| </Button> | |||||
| </Stack> | |||||
| */} | |||||
| </Stack> | |||||
| </FormProvider> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default CreateItem; | |||||
| @@ -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 CreateItemLoading: 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>CreateMaterial | |||||
| <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 CreateItemLoading; | |||||
| @@ -0,0 +1,44 @@ | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import CreateEquipment from "./CreateEquipment"; | |||||
| import CreateEquipmentLoading from "./CreateEquipmentLoading"; | |||||
| import { CreateEquipmentInputs } from "@/app/api/settings/equipment/actions"; | |||||
| import { notFound } from "next/navigation"; | |||||
| import { fetchEquipment } from "@/app/api/settings/equipment"; | |||||
| interface SubComponents { | |||||
| Loading: typeof CreateEquipmentLoading; | |||||
| } | |||||
| type Props = { | |||||
| id?: number | |||||
| // type: TypeEnum; | |||||
| }; | |||||
| const CreateEquipmentWrapper: React.FC<Props> & | |||||
| SubComponents = async ({ id }) => { | |||||
| var result | |||||
| var defaultValues: Partial<CreateEquipmentInputs> | undefined | |||||
| // console.log(type) | |||||
| var qcChecks | |||||
| if (id) { | |||||
| result = await fetchEquipment(id); | |||||
| const equipment = result | |||||
| console.log(equipment) | |||||
| defaultValues = { | |||||
| id: equipment?.id, | |||||
| code: equipment?.code, | |||||
| name: equipment?.name, | |||||
| description: equipment?.description, | |||||
| equipmentTypeId: equipment?.equipmentTypeId ?? equipment?.equipmentType?.id, | |||||
| }; | |||||
| } | |||||
| return ( | |||||
| <CreateEquipment | |||||
| isEditMode={Boolean(id)} | |||||
| defaultValues={defaultValues} | |||||
| /> | |||||
| ); | |||||
| }; | |||||
| CreateEquipmentWrapper.Loading = CreateEquipmentLoading; | |||||
| export default CreateEquipmentWrapper; | |||||
| @@ -0,0 +1,206 @@ | |||||
| "use client"; | |||||
| import { | |||||
| Box, | |||||
| Button, | |||||
| Card, | |||||
| CardContent, | |||||
| Grid, | |||||
| Stack, | |||||
| TextField, | |||||
| Typography, | |||||
| } from "@mui/material"; | |||||
| import { Check, Close, EditNote } from "@mui/icons-material"; | |||||
| import { useFormContext } from "react-hook-form"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import InputDataGrid from "../InputDataGrid"; | |||||
| import { useCallback, useMemo, useState } from "react"; | |||||
| import { GridColDef, GridRowModel } from "@mui/x-data-grid"; | |||||
| import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import { NumberInputProps } from "./NumberInputProps"; | |||||
| import { CreateEquipmentInputs } from "@/app/api/settings/equipment/actions"; | |||||
| import { RestartAlt } from "@mui/icons-material"; | |||||
| type Props = { | |||||
| // isEditMode: boolean; | |||||
| // type: TypeEnum; | |||||
| isEditMode: boolean; | |||||
| // type: TypeEnum; | |||||
| defaultValues: Partial<CreateEquipmentInputs> | undefined; | |||||
| }; | |||||
| const ProductDetails: React.FC<Props> = ({isEditMode}) => { | |||||
| const { | |||||
| t, | |||||
| i18n: { language }, | |||||
| } = useTranslation(); | |||||
| const { | |||||
| register, | |||||
| formState: { errors, defaultValues, touchedFields }, | |||||
| watch, | |||||
| control, | |||||
| setValue, | |||||
| getValues, | |||||
| reset, | |||||
| resetField, | |||||
| setError, | |||||
| clearErrors, | |||||
| } = useFormContext<CreateEquipmentInputs>(); | |||||
| // const typeColumns = useMemo<GridColDef[]>( | |||||
| // () => [ | |||||
| // { | |||||
| // field: "type", | |||||
| // headerName: "type", | |||||
| // flex: 1, | |||||
| // editable: true, | |||||
| // }, | |||||
| // ], | |||||
| // [] | |||||
| // ); | |||||
| // const weightUnitColumns = useMemo<GridColDef[]>( | |||||
| // () => [ | |||||
| // { | |||||
| // field: "weightUnit", | |||||
| // headerName: "Weight Unit", | |||||
| // flex: 1, | |||||
| // editable: true, | |||||
| // }, | |||||
| // { | |||||
| // field: "conversion", | |||||
| // headerName: "conversion", // show base unit | |||||
| // flex: 1, | |||||
| // type: "number", | |||||
| // editable: true, | |||||
| // }, | |||||
| // ], | |||||
| // [] | |||||
| // ); | |||||
| // const uomColumns = useMemo<GridColDef[]>( | |||||
| // () => [ | |||||
| // { | |||||
| // field: "uom", | |||||
| // headerName: "uom", | |||||
| // flex: 1, | |||||
| // editable: true, | |||||
| // }, | |||||
| // ], | |||||
| // [] | |||||
| // ); | |||||
| // const validationTest = useCallback( | |||||
| // ( | |||||
| // newRow: GridRowModel<TableRow<Partial<CreateItemInputs>, EntryError>> | |||||
| // ): EntryError => { | |||||
| // const error: EntryError = {}; | |||||
| // console.log(newRow); | |||||
| // return Object.keys(error).length > 0 ? error : undefined; | |||||
| // }, | |||||
| // [] | |||||
| // ); | |||||
| const handleCancel = () => { | |||||
| router.replace(`/settings/equipment`); | |||||
| }; | |||||
| return ( | |||||
| <Card sx={{ display: "block" }}> | |||||
| <CardContent component={Stack} spacing={4}> | |||||
| <Box> | |||||
| <Typography variant="overline" display="block" marginBlockEnd={1}> | |||||
| {t("Equipment Details")} | |||||
| </Typography> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Equipment Type")} | |||||
| fullWidth | |||||
| {...register("equipmentTypeId")} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||||
| {/* | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Name")} | |||||
| fullWidth | |||||
| {...register("name", { | |||||
| required: "name required!", | |||||
| })} | |||||
| error={Boolean(errors.name)} | |||||
| helperText={errors.name?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("Code")} | |||||
| fullWidth | |||||
| {...register("code", { | |||||
| required: "code required!", | |||||
| })} | |||||
| error={Boolean(errors.code)} | |||||
| helperText={errors.code?.message} | |||||
| /> | |||||
| </Grid> | |||||
| */ | |||||
| } | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("description")} | |||||
| fullWidth | |||||
| {...register("description")} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <Stack direction="row" justifyContent="flex-start" spacing={2} sx={{ mt: 2 }}> | |||||
| <Button | |||||
| name="submit" | |||||
| variant="contained" | |||||
| startIcon={<Check />} | |||||
| type="submit" | |||||
| // disabled={submitDisabled} | |||||
| > | |||||
| {isEditMode ? t("Save") : t("Confirm")} | |||||
| </Button> | |||||
| <Button | |||||
| variant="outlined" | |||||
| startIcon={<Close />} | |||||
| onClick={handleCancel} | |||||
| > | |||||
| {t("Cancel")} | |||||
| </Button> | |||||
| <Button | |||||
| variant="outlined" | |||||
| startIcon={<RestartAlt />} | |||||
| onClick={() => reset()} | |||||
| > | |||||
| {t("Reset")} | |||||
| </Button> | |||||
| </Stack> | |||||
| </Grid> | |||||
| {/* <Grid item xs={6}> | |||||
| <InputDataGrid<CreateItemInputs, EntryError> | |||||
| _formKey={"type"} | |||||
| columns={typeColumns} | |||||
| validateRow={validationTest} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <InputDataGrid<CreateItemInputs, EntryError> | |||||
| _formKey={"uom"} | |||||
| columns={uomColumns} | |||||
| validateRow={validationTest} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={12}> | |||||
| <InputDataGrid<CreateItemInputs, EntryError> | |||||
| _formKey={"weightUnit"} | |||||
| columns={weightUnitColumns} | |||||
| validateRow={validationTest} | |||||
| /> | |||||
| </Grid>*/} | |||||
| </Grid> | |||||
| </Box> | |||||
| </CardContent> | |||||
| </Card> | |||||
| ); | |||||
| }; | |||||
| export default ProductDetails; | |||||
| @@ -0,0 +1,5 @@ | |||||
| import { InputBaseComponentProps } from "@mui/material"; | |||||
| export var NumberInputProps: InputBaseComponentProps = { | |||||
| step: 0.01, | |||||
| }; | |||||
| @@ -0,0 +1 @@ | |||||
| export { default } from "./CreateEquipmentWrapper"; | |||||
| @@ -0,0 +1,151 @@ | |||||
| "use client"; | |||||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import SearchBox, { Criterion } from "../SearchBox"; | |||||
| import { EquipmentResult } from "@/app/api/settings/equipment"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| 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 axiosInstance from "@/app/(main)/axios/axiosInstance"; | |||||
| type Props = { | |||||
| equipments: EquipmentResult[]; | |||||
| }; | |||||
| type SearchQuery = Partial<Omit<EquipmentResult, "id">>; | |||||
| type SearchParamNames = keyof SearchQuery; | |||||
| const EquipmentSearch: React.FC<Props> = ({ equipments }) => { | |||||
| const [filteredEquipments, setFilteredEquipments] = useState<EquipmentResult[]>(equipments); | |||||
| const { t } = useTranslation("common"); | |||||
| const router = useRouter(); | |||||
| const [filterObj, setFilterObj] = useState({}); | |||||
| const [pagingController, setPagingController] = useState({ | |||||
| pageNum: 1, | |||||
| pageSize: 10, | |||||
| // totalCount: 0, | |||||
| }); | |||||
| const [totalCount, setTotalCount] = useState(0) | |||||
| const searchCriteria: Criterion<SearchParamNames>[] = useMemo(() => { | |||||
| var searchCriteria: Criterion<SearchParamNames>[] = [ | |||||
| { label: t("Code"), paramName: "code", type: "text" }, | |||||
| { label: t("Description"), paramName: "description", type: "text" }, | |||||
| ]; | |||||
| return searchCriteria; | |||||
| }, [t, equipments]); | |||||
| const onDetailClick = useCallback( | |||||
| (equipment: EquipmentResult) => { | |||||
| router.push(`/settings/equipment/edit?id=${equipment.id}`); | |||||
| }, | |||||
| [router] | |||||
| ); | |||||
| const onDeleteClick = useCallback((equipment: EquipmentResult) => {}, [router]); | |||||
| const columns = useMemo<Column<EquipmentResult>[]>( | |||||
| () => [ | |||||
| { | |||||
| name: "id", | |||||
| label: t("Details"), | |||||
| onClick: onDetailClick, | |||||
| buttonIcon: <EditNote />, | |||||
| }, | |||||
| { | |||||
| name: "code", | |||||
| label: t("Code"), | |||||
| }, | |||||
| { | |||||
| name: "description", | |||||
| label: t("Description"), | |||||
| }, | |||||
| { | |||||
| name: "equipmentTypeId", | |||||
| label: t("Equipment Type"), | |||||
| }, | |||||
| { | |||||
| name: "action", | |||||
| label: t(""), | |||||
| buttonIcon: <GridDeleteIcon />, | |||||
| onClick: onDeleteClick, | |||||
| }, | |||||
| ], | |||||
| [filteredEquipments] | |||||
| ); | |||||
| const refetchData = useCallback( | |||||
| 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, | |||||
| }; | |||||
| try { | |||||
| const response = await axiosInstance.get<EquipmentResult[]>( | |||||
| `${NEXT_PUBLIC_API_URL}/Equipment/getRecordByPage`, | |||||
| { params } | |||||
| ); | |||||
| console.log(response); | |||||
| if (response.status == 200) { | |||||
| setFilteredEquipments(response.data.records); | |||||
| setTotalCount(response.data.total) | |||||
| return response; // Return the data from the response | |||||
| } else { | |||||
| throw "400"; | |||||
| } | |||||
| } catch (error) { | |||||
| console.error("Error fetching equipment types:", error); | |||||
| throw error; // Rethrow the error for further handling | |||||
| } | |||||
| }, | |||||
| [axiosInstance, pagingController.pageNum, pagingController.pageSize] | |||||
| ); | |||||
| useEffect(() => { | |||||
| refetchData(filterObj); | |||||
| }, [filterObj, pagingController.pageNum, pagingController.pageSize]); | |||||
| const onReset = useCallback(() => { | |||||
| setFilteredEquipments(equipments); | |||||
| }, [equipments]); | |||||
| return ( | |||||
| <> | |||||
| <SearchBox | |||||
| criteria={searchCriteria} | |||||
| onSearch={(query) => { | |||||
| // setFilteredItems( | |||||
| // equipmentTypes.filter((pm) => { | |||||
| // return ( | |||||
| // pm.code.toLowerCase().includes(query.code.toLowerCase()) && | |||||
| // pm.name.toLowerCase().includes(query.name.toLowerCase()) | |||||
| // ); | |||||
| // }) | |||||
| // ); | |||||
| setFilterObj({ | |||||
| ...query, | |||||
| }); | |||||
| }} | |||||
| onReset={onReset} | |||||
| /> | |||||
| <SearchResults<EquipmentResult> | |||||
| items={filteredEquipments} | |||||
| columns={columns} | |||||
| setPagingController={setPagingController} | |||||
| pagingController={pagingController} | |||||
| totalCount={totalCount} | |||||
| isAutoPaging={false} | |||||
| /> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default EquipmentSearch; | |||||
| @@ -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 EquipmentTypeSearchLoading: 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 EquipmentTypeSearchLoading; | |||||
| @@ -0,0 +1,26 @@ | |||||
| import { fetchAllEquipments, } from "@/app/api/settings/equipment"; | |||||
| import EquipmentSearchLoading from "./EquipmentSearchLoading"; | |||||
| import { SearchParams } from "@/app/utils/fetchUtil"; | |||||
| import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
| import { notFound } from "next/navigation"; | |||||
| import EquipmentSearch from "./EquipmentSearch"; | |||||
| interface SubComponents { | |||||
| Loading: typeof EquipmentSearchLoading; | |||||
| } | |||||
| type Props = { | |||||
| // type: TypeEnum; | |||||
| }; | |||||
| const EquipmentSearchWrapper: React.FC<Props> & SubComponents = async ({ | |||||
| // type, | |||||
| }) => { | |||||
| // console.log(type) | |||||
| // var result = await fetchAllEquipmentTypes() | |||||
| return <EquipmentSearch equipments={[]} />; | |||||
| }; | |||||
| EquipmentSearchWrapper.Loading = EquipmentSearchLoading; | |||||
| export default EquipmentSearchWrapper; | |||||
| @@ -0,0 +1 @@ | |||||
| export { default } from "./EquipmentSearchWrapper"; | |||||
| @@ -226,7 +226,7 @@ const NavigationContent: React.FC = () => { | |||||
| { | { | ||||
| icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
| label: "Equipment", | label: "Equipment", | ||||
| path: "/settings/user", | |||||
| path: "/settings/equipment", | |||||
| }, | }, | ||||
| { | { | ||||
| icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
| @@ -59,10 +59,12 @@ | |||||
| "items": "物料", | "items": "物料", | ||||
| "edit":"編輯", | "edit":"編輯", | ||||
| "Edit Equipment Type":"設備類型詳情", | "Edit Equipment Type":"設備類型詳情", | ||||
| "Edit Equipment":"設備詳情", | |||||
| "equipmentType":"設備類型", | "equipmentType":"設備類型", | ||||
| "Description":"描述", | "Description":"描述", | ||||
| "Details": "詳情", | "Details": "詳情", | ||||
| "Equipment Type Details":"設備類型詳情", | "Equipment Type Details":"設備類型詳情", | ||||
| "Save":"儲存", | "Save":"儲存", | ||||
| "Cancel":"取消" | |||||
| "Cancel":"取消", | |||||
| "Equipment Details":"設備詳情" | |||||
| } | } | ||||