@@ -37,6 +37,7 @@ const material: React.FC<Props> = async ({ searchParams }) => { | |||||
> | > | ||||
{t("Create Claim")} | {t("Create Claim")} | ||||
</Button> | </Button> | ||||
</Stack> | </Stack> | ||||
{/* <Suspense fallback={<MaterialSearch.Loading />}> | {/* <Suspense fallback={<MaterialSearch.Loading />}> | ||||
<MaterialSearch /> | <MaterialSearch /> | ||||
@@ -0,0 +1,22 @@ | |||||
import { SearchParams } from "@/app/utils/fetchUtil"; | |||||
import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
import CreateEquipmentType from "@/components/CreateEquipmentType"; | |||||
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/CreateEquipmentType"; | |||||
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 EquipmentTypeSearch from "@/components/EquipmentTypeSearch"; | |||||
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 { fetchAllEquipmentTypes } from "@/app/api/settings/equipmentType"; | |||||
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 equipmentTypes = await fetchAllEquipmentTypes(); | |||||
// preloadClaims(); | |||||
return ( | |||||
<> | |||||
<Stack | |||||
direction="row" | |||||
justifyContent="space-between" | |||||
flexWrap="wrap" | |||||
rowGap={2} | |||||
> | |||||
<Typography variant="h4" marginInlineEnd={2}> | |||||
{t("Equipment Type")} | |||||
</Typography> | |||||
{/* <Button | |||||
variant="contained" | |||||
startIcon={<Add />} | |||||
LinkComponent={Link} | |||||
href="product/create" | |||||
> | |||||
{t("Create product")} | |||||
</Button> */} | |||||
</Stack> | |||||
<Suspense fallback={<EquipmentTypeSearch.Loading />}> | |||||
<I18nProvider namespaces={["common","project"]}> | |||||
<EquipmentTypeSearch /> | |||||
</I18nProvider> | |||||
</Suspense> | |||||
</> | |||||
); | |||||
}; | |||||
export default productSetting; |
@@ -10,7 +10,7 @@ import Link from "next/link"; | |||||
import { Suspense } from "react"; | import { Suspense } from "react"; | ||||
import RoughScheduleLoading from "@/components/RoughScheduleSetting/RoughScheduleLoading"; | import RoughScheduleLoading from "@/components/RoughScheduleSetting/RoughScheduleLoading"; | ||||
import RoughScheduleSetting from "@/components/RoughScheduleSetting/RoughScheduleSetting"; | import RoughScheduleSetting from "@/components/RoughScheduleSetting/RoughScheduleSetting"; | ||||
import { I18nProvider } from "@/i18n"; | |||||
export const metadata: Metadata = { | export const metadata: Metadata = { | ||||
title: "Demand Forecast Setting", | title: "Demand Forecast Setting", | ||||
}; | }; | ||||
@@ -40,9 +40,11 @@ const roughScheduleSetting: React.FC = async () => { | |||||
{t("Create product")} | {t("Create product")} | ||||
</Button> */} | </Button> */} | ||||
</Stack> | </Stack> | ||||
<Suspense fallback={<RoughScheduleLoading.Loading />}> | |||||
<RoughScheduleSetting /> | |||||
</Suspense> | |||||
<I18nProvider namespaces={[ "common", "project"]}> | |||||
<Suspense fallback={<RoughScheduleLoading.Loading />}> | |||||
<RoughScheduleSetting /> | |||||
</Suspense> | |||||
</I18nProvider> | |||||
</> | </> | ||||
); | ); | ||||
}; | }; | ||||
@@ -0,0 +1,37 @@ | |||||
"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 CreateEquipmentTypeInputs = { | |||||
id?: string | number | |||||
code: string; | |||||
name: string; | |||||
description?: string | undefined; | |||||
} | |||||
export const saveEquipmentType = async (data: CreateEquipmentTypeInputs) => { | |||||
// try { | |||||
const equipmentType = await serverFetchJson<CreateEquipmentTypeResponse<CreateEquipmentTypeInputs>>(`${BASE_API_URL}/EquipmentType/save`, { | |||||
method: "POST", | |||||
body: JSON.stringify(data), | |||||
headers: { "Content-Type": "application/json" }, | |||||
}); | |||||
revalidateTag("EquipmentType"); | |||||
return equipmentType | |||||
}; |
@@ -0,0 +1,33 @@ | |||||
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/CreateEquipmentType/CreateEquipmentType"; | |||||
// import { TypeInputs, UomInputs, WeightUnitInputs } from "./actions"; | |||||
export type EquipmentTypeResult = { | |||||
id: string | number | |||||
code: string; | |||||
name: string; | |||||
description: string | undefined; | |||||
action?: any | |||||
} | |||||
export type Result = { | |||||
equipmentType: EquipmentTypeResult | |||||
} | |||||
export const fetchAllEquipmentTypes = cache(async () => { | |||||
return serverFetchJson<EquipmentTypeResult[]>(`${BASE_API_URL}/EquipmentType`, { | |||||
next: { tags: ["equipmentTypes"] }, | |||||
}); | |||||
}); | |||||
export const fetchEquipmentType = cache(async (id: number) => { | |||||
return serverFetchJson<EquipmentTypeResult>(`${BASE_API_URL}/EquipmentType/details/${id}`, { | |||||
next: { tags: ["equipmentTypes"] }, | |||||
}); | |||||
}); |
@@ -5,6 +5,13 @@ export interface CreateItemResponse<T> { | |||||
message: string | null; | message: string | null; | ||||
errorPosition: string | keyof T; | errorPosition: string | keyof T; | ||||
} | } | ||||
export interface CreateEquipmentTypeResponse<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 | ||||
@@ -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 { | |||||
CreateEquipmentTypeInputs, | |||||
saveEquipmentType, | |||||
} from "@/app/api/settings/equipmentType/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 EquipmentTypeDetails from "./EquipmentTypeDetails"; | |||||
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<CreateEquipmentTypeInputs> | 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 Type" | |||||
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 Type"; | |||||
redirPath = "/settings/equipmentType"; | |||||
// } | |||||
// 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<CreateEquipmentTypeInputs>({ | |||||
defaultValues: defaultValues ? defaultValues : { | |||||
}, | |||||
}); | |||||
const errors = formProps.formState.errors; | |||||
const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
(_e, newValue) => { | |||||
setTabIndex(newValue); | |||||
}, | |||||
[], | |||||
); | |||||
const handleCancel = () => { | |||||
router.replace(`/settings/equipmentType`); | |||||
}; | |||||
const onSubmit = useCallback<SubmitHandler<CreateEquipmentTypeInputs & {}>>( | |||||
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 saveEquipmentType(data); | |||||
console.log("asdad") | |||||
// var responseQ = await saveItemQcChecks(qcCheck) | |||||
if (responseI) { | |||||
if (!Boolean(responseI.id)) { | |||||
formProps.setError(responseI.errorPosition!! as keyof CreateEquipmentTypeInputs, { | |||||
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<CreateEquipmentTypeInputs>>( | |||||
(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 Type Details")} iconPosition="end"/> | |||||
{/* <Tab label={t("Qc items")} iconPosition="end" /> */} | |||||
</Tabs> | |||||
{serverError && ( | |||||
<Typography variant="body2" color="error" alignSelf="flex-end"> | |||||
{serverError} | |||||
</Typography> | |||||
)} | |||||
{tabIndex === 0 && <EquipmentTypeDetails 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,43 @@ | |||||
import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
import CreateEquipmentType from "./CreateEquipmentType"; | |||||
import CreateEquipmentTypeLoading from "./CreateEquipmentTypeLoading"; | |||||
import { CreateEquipmentTypeInputs } from "@/app/api/settings/equipmentType/actions"; | |||||
import { notFound } from "next/navigation"; | |||||
import { fetchEquipmentType } from "@/app/api/settings/equipmentType"; | |||||
interface SubComponents { | |||||
Loading: typeof CreateEquipmentTypeLoading; | |||||
} | |||||
type Props = { | |||||
id?: number | |||||
// type: TypeEnum; | |||||
}; | |||||
const CreateEquipmentTypeWrapper: React.FC<Props> & | |||||
SubComponents = async ({ id }) => { | |||||
var result | |||||
var defaultValues: Partial<CreateEquipmentTypeInputs> | undefined | |||||
// console.log(type) | |||||
var qcChecks | |||||
if (id) { | |||||
result = await fetchEquipmentType(id); | |||||
const equipmentType = result | |||||
console.log(equipmentType) | |||||
defaultValues = { | |||||
id: equipmentType?.id, | |||||
code: equipmentType?.code, | |||||
name: equipmentType?.name, | |||||
description: equipmentType?.description, | |||||
}; | |||||
} | |||||
return ( | |||||
<CreateEquipmentType | |||||
isEditMode={Boolean(id)} | |||||
defaultValues={defaultValues} | |||||
/> | |||||
); | |||||
}; | |||||
CreateEquipmentTypeWrapper.Loading = CreateEquipmentTypeLoading; | |||||
export default CreateEquipmentTypeWrapper; |
@@ -0,0 +1,199 @@ | |||||
"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 { CreateEquipmentTypeInputs } from "@/app/api/settings/equipmentType/actions"; | |||||
import { RestartAlt } from "@mui/icons-material"; | |||||
type Props = { | |||||
// isEditMode: boolean; | |||||
// type: TypeEnum; | |||||
isEditMode: boolean; | |||||
// type: TypeEnum; | |||||
defaultValues: Partial<CreateEquipmentTypeInputs> | 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<CreateEquipmentTypeInputs>(); | |||||
// 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/equipmentType`); | |||||
}; | |||||
return ( | |||||
<Card sx={{ display: "block" }}> | |||||
<CardContent component={Stack} spacing={4}> | |||||
<Box> | |||||
<Typography variant="overline" display="block" marginBlockEnd={1}> | |||||
{t("Equipment Type Details")} | |||||
</Typography> | |||||
<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 "./CreateEquipmentTypeWrapper"; |
@@ -174,10 +174,11 @@ const CreateItem: React.FC<Props> = ({ | |||||
{serverError} | {serverError} | ||||
</Typography> | </Typography> | ||||
)} | )} | ||||
{tabIndex === 0 && <ProductDetails />} | |||||
{tabIndex === 0 && <ProductDetails isEditMode={isEditMode} />} | |||||
{tabIndex === 1 && <QcDetails apiRef={apiRef} />} | {tabIndex === 1 && <QcDetails apiRef={apiRef} />} | ||||
{/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */} | {/* {type === TypeEnum.MATERIAL && <MaterialDetails />} */} | ||||
{/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */} | {/* {type === TypeEnum.BYPRODUCT && <ByProductDetails />} */} | ||||
{/* | |||||
<Stack direction="row" justifyContent="flex-end" gap={1}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
<Button | <Button | ||||
name="submit" | name="submit" | ||||
@@ -196,6 +197,7 @@ const CreateItem: React.FC<Props> = ({ | |||||
{t("Cancel")} | {t("Cancel")} | ||||
</Button> | </Button> | ||||
</Stack> | </Stack> | ||||
*/} | |||||
</Stack> | </Stack> | ||||
</FormProvider> | </FormProvider> | ||||
</> | </> | ||||
@@ -1,6 +1,7 @@ | |||||
"use client"; | "use client"; | ||||
import { | import { | ||||
Box, | Box, | ||||
Button, | |||||
Card, | Card, | ||||
CardContent, | CardContent, | ||||
Grid, | Grid, | ||||
@@ -8,22 +9,28 @@ import { | |||||
TextField, | TextField, | ||||
Typography, | Typography, | ||||
} from "@mui/material"; | } from "@mui/material"; | ||||
import { Check, Close, EditNote } from "@mui/icons-material"; | |||||
import { useFormContext } from "react-hook-form"; | import { useFormContext } from "react-hook-form"; | ||||
import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
import InputDataGrid from "../InputDataGrid"; | import InputDataGrid from "../InputDataGrid"; | ||||
import { useCallback, useMemo, useState } from "react"; | import { useCallback, useMemo, useState } from "react"; | ||||
import { GridColDef, GridRowModel } from "@mui/x-data-grid"; | import { GridColDef, GridRowModel } from "@mui/x-data-grid"; | ||||
import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; | import { InputDataGridProps, TableRow } from "../InputDataGrid/InputDataGrid"; | ||||
import { TypeEnum } from "@/app/utils/typeEnum"; | import { TypeEnum } from "@/app/utils/typeEnum"; | ||||
import { NumberInputProps } from "./NumberInputProps"; | import { NumberInputProps } from "./NumberInputProps"; | ||||
import { CreateItemInputs } from "@/app/api/settings/item/actions"; | import { CreateItemInputs } from "@/app/api/settings/item/actions"; | ||||
import { RestartAlt } from "@mui/icons-material"; | |||||
type Props = { | type Props = { | ||||
// isEditMode: boolean; | // isEditMode: boolean; | ||||
// type: TypeEnum; | // type: TypeEnum; | ||||
isEditMode: boolean; | |||||
// type: TypeEnum; | |||||
defaultValues: Partial<CreateItemInputs> | undefined; | |||||
qcChecks: ItemQc[] | |||||
}; | }; | ||||
const ProductDetails: React.FC<Props> = ({}) => { | |||||
const ProductDetails: React.FC<Props> = ({isEditMode}) => { | |||||
const { | const { | ||||
t, | t, | ||||
i18n: { language }, | i18n: { language }, | ||||
@@ -92,7 +99,9 @@ const ProductDetails: React.FC<Props> = ({}) => { | |||||
// }, | // }, | ||||
// [] | // [] | ||||
// ); | // ); | ||||
const handleCancel = () => { | |||||
router.replace(`/settings/product`); | |||||
}; | |||||
return ( | return ( | ||||
<Card sx={{ display: "block" }}> | <Card sx={{ display: "block" }}> | ||||
<CardContent component={Stack} spacing={4}> | <CardContent component={Stack} spacing={4}> | ||||
@@ -191,6 +200,33 @@ const ProductDetails: React.FC<Props> = ({}) => { | |||||
helperText={errors.maxQty?.message} | helperText={errors.maxQty?.message} | ||||
/> | /> | ||||
</Grid> | </Grid> | ||||
<Grid item xs={0}> | |||||
<Stack direction="row" justifyContent="flex-end" 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}> | {/* <Grid item xs={6}> | ||||
<InputDataGrid<CreateItemInputs, EntryError> | <InputDataGrid<CreateItemInputs, EntryError> | ||||
_formKey={"type"} | _formKey={"type"} | ||||
@@ -0,0 +1,147 @@ | |||||
"use client"; | |||||
import { useCallback, useEffect, useMemo, useState } from "react"; | |||||
import SearchBox, { Criterion } from "../SearchBox"; | |||||
import { EquipmentTypeResult } from "@/app/api/settings/equipmentType"; | |||||
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 = { | |||||
equipmentTypes: EquipmentTypeResult[]; | |||||
}; | |||||
type SearchQuery = Partial<Omit<EquipmentTypeResult, "id">>; | |||||
type SearchParamNames = keyof SearchQuery; | |||||
const EquipmentTypeSearch: React.FC<Props> = ({ equipmentTypes }) => { | |||||
const [filteredEquipmentTypes, setFilteredEquipmentTypes] = useState<EquipmentTypeResult[]>(equipmentTypes); | |||||
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, equipmentTypes]); | |||||
const onDetailClick = useCallback( | |||||
(equipmentType: EquipmentTypeResult) => { | |||||
router.push(`/settings/equipmentType/edit?id=${equipmentType.id}`); | |||||
}, | |||||
[router] | |||||
); | |||||
const onDeleteClick = useCallback((equipmentType: EquipmentTypeResult) => {}, [router]); | |||||
const columns = useMemo<Column<EquipmentTypeResult>[]>( | |||||
() => [ | |||||
{ | |||||
name: "id", | |||||
label: t("Details"), | |||||
onClick: onDetailClick, | |||||
buttonIcon: <EditNote />, | |||||
}, | |||||
{ | |||||
name: "code", | |||||
label: t("Code"), | |||||
}, | |||||
{ | |||||
name: "description", | |||||
label: t("Description"), | |||||
}, | |||||
{ | |||||
name: "action", | |||||
label: t(""), | |||||
buttonIcon: <GridDeleteIcon />, | |||||
onClick: onDeleteClick, | |||||
}, | |||||
], | |||||
[filteredEquipmentTypes] | |||||
); | |||||
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<EquipmentTypeResult[]>( | |||||
`${NEXT_PUBLIC_API_URL}/EquipmentType/getRecordByPage`, | |||||
{ params } | |||||
); | |||||
console.log(response); | |||||
if (response.status == 200) { | |||||
setFilteredEquipmentTypes(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(() => { | |||||
setFilteredEquipmentTypes(equipmentTypes); | |||||
}, [equipmentTypes]); | |||||
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<EquipmentTypeResult> | |||||
items={filteredEquipmentTypes} | |||||
columns={columns} | |||||
setPagingController={setPagingController} | |||||
pagingController={pagingController} | |||||
totalCount={totalCount} | |||||
isAutoPaging={false} | |||||
/> | |||||
</> | |||||
); | |||||
}; | |||||
export default EquipmentTypeSearch; |
@@ -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 { fetchAllEquipmentTypes, } from "@/app/api/settings/equipmentType"; | |||||
import EquipmentTypeSearchLoading from "./EquipmentTypeSearchLoading"; | |||||
import { SearchParams } from "@/app/utils/fetchUtil"; | |||||
import { TypeEnum } from "@/app/utils/typeEnum"; | |||||
import { notFound } from "next/navigation"; | |||||
import EquipmentTypeSearch from "./EquipmentTypeSearch"; | |||||
interface SubComponents { | |||||
Loading: typeof EquipmentTypeSearchLoading; | |||||
} | |||||
type Props = { | |||||
// type: TypeEnum; | |||||
}; | |||||
const EquipmentTypeSearchWrapper: React.FC<Props> & SubComponents = async ({ | |||||
// type, | |||||
}) => { | |||||
// console.log(type) | |||||
// var result = await fetchAllEquipmentTypes() | |||||
return <EquipmentTypeSearch equipmentTypes={[]} />; | |||||
}; | |||||
EquipmentTypeSearchWrapper.Loading = EquipmentTypeSearchLoading; | |||||
export default EquipmentTypeSearchWrapper; |
@@ -0,0 +1 @@ | |||||
export { default } from "./EquipmentTypeSearchWrapper"; |
@@ -221,7 +221,7 @@ const NavigationContent: React.FC = () => { | |||||
{ | { | ||||
icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
label: "Equipment Type", | label: "Equipment Type", | ||||
path: "/settings/user", | |||||
path: "/settings/equipmentType", | |||||
}, | }, | ||||
{ | { | ||||
icon: <RequestQuote />, | icon: <RequestQuote />, | ||||
@@ -1,22 +1,6 @@ | |||||
{ | { | ||||
"Overview": "概述", | |||||
"Qc Item": "品質檢驗項目", | |||||
"Dashboard": "儀表板", | |||||
"dashboard": "儀表板", | "dashboard": "儀表板", | ||||
"Raw Material": "原料", | |||||
"Purchase Order": "採購訂單", | |||||
"Pick Order": "提料單", | |||||
"View item In-out And inventory Ledger": "存貨", | |||||
"Inventory": "存貨", | |||||
"Delivery": "送貨", | |||||
"Delivery Order": "送貨單", | |||||
"Scheduling": "生產計劃", | |||||
"Demand Forecast Setting": "粗排設定", | |||||
"Demand Forecast": "粗排", | |||||
"FG & Material Demand Forecast Detail": "成品 & 原料粗排細節", | |||||
"Detail Scheduling": "細排", | |||||
"FG Production Schedule": "成品生產計劃", | |||||
"Settings": "設定", | |||||
"Edit": "編輯", | "Edit": "編輯", | ||||
"Search Criteria": "搜尋條件", | "Search Criteria": "搜尋條件", | ||||
@@ -46,7 +30,6 @@ | |||||
"Supplier": "供應商", | "Supplier": "供應商", | ||||
"Purchase Order":"採購單", | "Purchase Order":"採購單", | ||||
"Demand Forecast":"需求預測", | "Demand Forecast":"需求預測", | ||||
"Purchase Order":"採購單", | |||||
"Pick Order":"挑選貨單", | "Pick Order":"挑選貨單", | ||||
"Deliver Order":"交貨單", | "Deliver Order":"交貨單", | ||||
"Project":"專案", | "Project":"專案", | ||||
@@ -74,5 +57,12 @@ | |||||
"scheduling":"排程", | "scheduling":"排程", | ||||
"settings": "設定", | "settings": "設定", | ||||
"items": "物料", | "items": "物料", | ||||
"edit":"編輯" | |||||
"edit":"編輯", | |||||
"Edit Equipment Type":"設備類型詳情", | |||||
"equipmentType":"設備類型", | |||||
"Description":"描述", | |||||
"Details": "詳情", | |||||
"Equipment Type Details":"設備類型詳情", | |||||
"Save":"儲存", | |||||
"Cancel":"取消" | |||||
} | } |
@@ -5,8 +5,9 @@ | |||||
"Actions": "動作", | "Actions": "動作", | ||||
"Product": "產品", | "Product": "產品", | ||||
"Details": "詳情", | "Details": "詳情", | ||||
"View BoM": "查看 BoM" | |||||
"View BoM": "查看 BoM", | |||||
"description": "描述", | |||||
"details": "詳情" | |||||