@@ -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; | |||
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>{ | |||
records: T | |||
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 />, | |||
label: "Equipment", | |||
path: "/settings/user", | |||
path: "/settings/equipment", | |||
}, | |||
{ | |||
icon: <RequestQuote />, | |||
@@ -59,10 +59,12 @@ | |||
"items": "物料", | |||
"edit":"編輯", | |||
"Edit Equipment Type":"設備類型詳情", | |||
"Edit Equipment":"設備詳情", | |||
"equipmentType":"設備類型", | |||
"Description":"描述", | |||
"Details": "詳情", | |||
"Equipment Type Details":"設備類型詳情", | |||
"Save":"儲存", | |||
"Cancel":"取消" | |||
"Cancel":"取消", | |||
"Equipment Details":"設備詳情" | |||
} |