|
|
@@ -1,191 +1,185 @@ |
|
|
|
"use client"; |
|
|
|
|
|
|
|
import { useCallback, useEffect, useMemo, useState } from "react"; |
|
|
|
import React, {useCallback, useEffect, useMemo, useState} from "react"; |
|
|
|
import SearchBox, { Criterion } from "../SearchBox"; |
|
|
|
import { ItemsResult} from "@/app/api/settings/item"; |
|
|
|
import SearchResults, { Column } from "../SearchResults"; |
|
|
|
import { EditNote } from "@mui/icons-material"; |
|
|
|
import { useRouter, useSearchParams } from "next/navigation"; |
|
|
|
|
|
|
|
import { |
|
|
|
FormProvider, |
|
|
|
SubmitErrorHandler, |
|
|
|
SubmitHandler, |
|
|
|
useForm, |
|
|
|
} from "react-hook-form"; |
|
|
|
import { deleteDialog } from "../Swal/CustomAlerts"; |
|
|
|
import { Box, Button, Grid, Stack, Tab, Tabs, TabsProps, Typography } from "@mui/material"; |
|
|
|
import { Check, Close, EditNote } from "@mui/icons-material"; |
|
|
|
import { useGridApiRef } from "@mui/x-data-grid"; |
|
|
|
import {CreateItemInputs, saveItem} from "@/app/api/settings/item/actions"; |
|
|
|
import {saveItemQcChecks} from "@/app/api/settings/qcCheck/actions"; |
|
|
|
import {useTranslation} from "react-i18next/index"; |
|
|
|
import {ItemQc} from "@/app/api/settings/item"; |
|
|
|
import { GridDeleteIcon } from "@mui/x-data-grid"; |
|
|
|
import { TypeEnum } from "@/app/utils/typeEnum"; |
|
|
|
import axios from "axios"; |
|
|
|
import {BASE_API_URL, NEXT_PUBLIC_API_URL} from "@/config/api"; |
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
|
import axiosInstance from "@/app/(main)/axios/axiosInstance"; |
|
|
|
import Qs from 'qs'; // Make sure to import Qs |
|
|
|
|
|
|
|
type Props = { |
|
|
|
isEditMode: boolean; |
|
|
|
// type: TypeEnum; |
|
|
|
defaultValues: Partial<CreateItemInputs> | undefined; |
|
|
|
qcChecks: ItemQc[] |
|
|
|
items: ItemsResult[]; |
|
|
|
}; |
|
|
|
type SearchQuery = Partial<Omit<ItemsResult, "id">>; |
|
|
|
type SearchParamNames = keyof SearchQuery; |
|
|
|
|
|
|
|
const CreateItem: React.FC<Props> = ({ |
|
|
|
isEditMode, |
|
|
|
// type, |
|
|
|
defaultValues, |
|
|
|
qcChecks |
|
|
|
}) => { |
|
|
|
// console.log(type) |
|
|
|
const apiRef = useGridApiRef(); |
|
|
|
const params = useSearchParams() |
|
|
|
console.log(params.get("id")) |
|
|
|
const [serverError, setServerError] = useState<String>(""); |
|
|
|
const [tabIndex, setTabIndex] = useState(0); |
|
|
|
const { t } = useTranslation(); |
|
|
|
const RSSOverview: React.FC<Props> = ({ items }) => { |
|
|
|
const [filteredItems, setFilteredItems] = useState<ItemsResult[]>(items ?? []); |
|
|
|
const { t } = useTranslation("items"); |
|
|
|
const router = useRouter(); |
|
|
|
const title = "Product / Material" |
|
|
|
const [filterObj, setFilterObj] = useState({}); |
|
|
|
const [tempSelectedValue, setTempSelectedValue] = useState({}); |
|
|
|
const [pagingController, setPagingController] = useState({ |
|
|
|
pageNum: 1, |
|
|
|
pageSize: 10, |
|
|
|
totalCount: 0, |
|
|
|
}) |
|
|
|
|
|
|
|
const [mode, redirPath] = useMemo(() => { |
|
|
|
// var typeId = TypeEnum.CONSUMABLE_ID |
|
|
|
var title = ""; |
|
|
|
var mode = ""; |
|
|
|
var mode = "Search"; |
|
|
|
var redirPath = ""; |
|
|
|
// if (type === TypeEnum.MATERIAL) { |
|
|
|
// typeId = TypeEnum.MATERIAL_ID |
|
|
|
// title = "Material"; |
|
|
|
// redirPath = "/settings/material"; |
|
|
|
// } |
|
|
|
// if (type === TypeEnum.PRODUCT) { |
|
|
|
// typeId = TypeEnum.PRODUCT_ID |
|
|
|
title = "Rough Schedule"; |
|
|
|
title = "Product"; |
|
|
|
redirPath = "/settings/rss"; |
|
|
|
// } |
|
|
|
// if (type === TypeEnum.BYPRODUCT) { |
|
|
|
// typeId = TypeEnum.BYPRODUCT_ID |
|
|
|
// title = "By-Product"; |
|
|
|
// redirPath = "/settings/byProduct"; |
|
|
|
// } |
|
|
|
if (isEditMode) { |
|
|
|
mode = "Edit"; |
|
|
|
} else { |
|
|
|
mode = "Create"; |
|
|
|
} |
|
|
|
return [mode, redirPath]; |
|
|
|
}, [isEditMode]); |
|
|
|
// console.log(typeId) |
|
|
|
const formProps = useForm<CreateItemInputs>({ |
|
|
|
defaultValues: defaultValues ? defaultValues : { |
|
|
|
}, |
|
|
|
}); |
|
|
|
const errors = formProps.formState.errors; |
|
|
|
}, []); |
|
|
|
|
|
|
|
const handleCancel = () => { |
|
|
|
router.replace(`/settings/product`); |
|
|
|
const handleSelectionChange = (selectedValues: string[]) => { |
|
|
|
setTempSelectedValue({ |
|
|
|
...tempSelectedValue, |
|
|
|
excludeDate: selectedValues, |
|
|
|
}); |
|
|
|
}; |
|
|
|
const onSubmit = useCallback<SubmitHandler<CreateItemInputs & {}>>( |
|
|
|
async (data, event) => { |
|
|
|
let hasErrors = false; |
|
|
|
console.log(errors) |
|
|
|
// console.log(apiRef.current.getCellValue(2, "lowerLimit")) |
|
|
|
// apiRef.current. |
|
|
|
try { |
|
|
|
if (hasErrors) { |
|
|
|
setServerError(t("An error has occurred. Please try again later.") as String); |
|
|
|
return false; |
|
|
|
} |
|
|
|
console.log("data posted"); |
|
|
|
console.log(data); |
|
|
|
const qcCheck = data.qcChecks.length > 0 ? data.qcChecks.filter((q) => data.qcChecks_active.includes(q.id!!)).map((qc) => { |
|
|
|
return { |
|
|
|
qcItemId: qc.id, |
|
|
|
instruction: qc.instruction, |
|
|
|
lowerLimit: qc.lowerLimit, |
|
|
|
upperLimit: qc.upperLimit, |
|
|
|
itemId: parseInt(params.get("id")!.toString()) |
|
|
|
} |
|
|
|
}) : [] |
|
|
|
|
|
|
|
const test = data.qcChecks.filter((q) => data.qcChecks_active.includes(q.id!!)) |
|
|
|
// TODO: |
|
|
|
// 1. check field ( directly modify col def / check here ) |
|
|
|
// 2. set error change tab index |
|
|
|
console.log(test) |
|
|
|
console.log(qcCheck) |
|
|
|
// return |
|
|
|
// do api |
|
|
|
console.log("asdad") |
|
|
|
var responseI = await saveItem(data); |
|
|
|
console.log("asdad") |
|
|
|
var responseQ = await saveItemQcChecks(qcCheck) |
|
|
|
if (responseI && responseQ) { |
|
|
|
if (!Boolean(responseI.id)) { |
|
|
|
formProps.setError(responseI.errorPosition!! as keyof CreateItemInputs, { |
|
|
|
message: responseI.message!!, |
|
|
|
type: "required", |
|
|
|
}); |
|
|
|
} else if (!Boolean(responseQ.id)) { |
|
|
|
|
|
|
|
} else if (Boolean(responseI.id) && Boolean(responseQ.id)) { |
|
|
|
router.replace(redirPath); |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e) { |
|
|
|
// backend error |
|
|
|
setServerError(t("An error has occurred. Please try again later.")); |
|
|
|
console.log(e); |
|
|
|
} |
|
|
|
|
|
|
|
const dayOptions = [ |
|
|
|
{label: "Mon", value: 1}, |
|
|
|
{label: "Tue", value: 2}, |
|
|
|
{label: "Wed", value: 3}, |
|
|
|
{label: "Thu", value: 4}, |
|
|
|
{label: "Fri", value: 5}, |
|
|
|
{label: "Sat", value: 6}, |
|
|
|
{label: "Sun", value: 7}, |
|
|
|
]; |
|
|
|
|
|
|
|
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( |
|
|
|
() => { |
|
|
|
var searchCriteria: Criterion<SearchParamNames>[] = [ |
|
|
|
{ label: t("Finished Goods Name"), paramName: "fgName", type: "text" }, |
|
|
|
{ |
|
|
|
label: t("Exclude Date"), |
|
|
|
paramName: "excludeDate", |
|
|
|
type: "multi-select", |
|
|
|
options: dayOptions, |
|
|
|
selectedValues: filterObj, |
|
|
|
handleSelectionChange: handleSelectionChange, |
|
|
|
}, |
|
|
|
] |
|
|
|
return searchCriteria |
|
|
|
}, |
|
|
|
[apiRef, router, t] |
|
|
|
[t, items] |
|
|
|
); |
|
|
|
|
|
|
|
// multiple tabs |
|
|
|
const onSubmitError = useCallback<SubmitErrorHandler<CreateItemInputs>>( |
|
|
|
(errors) => {}, |
|
|
|
[] |
|
|
|
const onDetailClick = useCallback( |
|
|
|
(item: ItemsResult) => { |
|
|
|
router.push(`/settings/items/edit?id=${item.id}`); |
|
|
|
}, |
|
|
|
[router] |
|
|
|
); |
|
|
|
|
|
|
|
const onDeleteClick = useCallback( |
|
|
|
(item: ItemsResult) => {}, |
|
|
|
[router] |
|
|
|
); |
|
|
|
|
|
|
|
const columns = useMemo<Column<ItemsResult>[]>( |
|
|
|
() => [ |
|
|
|
{ |
|
|
|
name: "id", |
|
|
|
label: t("Details"), |
|
|
|
onClick: onDetailClick, |
|
|
|
buttonIcon: <EditNote />, |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "fgName", |
|
|
|
label: "Finished Goods Name", |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "excludeDate", |
|
|
|
label: t("Exclude Date"), |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "action", |
|
|
|
label: t(""), |
|
|
|
buttonIcon: <GridDeleteIcon />, |
|
|
|
onClick: onDeleteClick, |
|
|
|
}, |
|
|
|
], |
|
|
|
[filteredItems] |
|
|
|
); |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
refetchData(filterObj); |
|
|
|
|
|
|
|
}, [filterObj, pagingController.pageNum, pagingController.pageSize]); |
|
|
|
|
|
|
|
const refetchData = async (filterObj: SearchQuery) => { |
|
|
|
|
|
|
|
const authHeader = axiosInstance.defaults.headers['Authorization']; |
|
|
|
if (!authHeader) { |
|
|
|
return; // Exit the function if the token is not set |
|
|
|
} |
|
|
|
|
|
|
|
const params ={ |
|
|
|
pageNum: pagingController.pageNum, |
|
|
|
pageSize: pagingController.pageSize, |
|
|
|
...filterObj, |
|
|
|
...tempSelectedValue, |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
const response = await axiosInstance.get<ItemsResult[]>(`${NEXT_PUBLIC_API_URL}/items/getRecordByPage`, { |
|
|
|
params, |
|
|
|
paramsSerializer: (params) => { |
|
|
|
return Qs.stringify(params, { arrayFormat: 'repeat' }); |
|
|
|
}, |
|
|
|
}); |
|
|
|
setFilteredItems(response.data.records); |
|
|
|
setPagingController({ |
|
|
|
...pagingController, |
|
|
|
totalCount: response.data.total |
|
|
|
}) |
|
|
|
return response; // Return the data from the response |
|
|
|
} catch (error) { |
|
|
|
console.error('Error fetching items:', error); |
|
|
|
throw error; // Rethrow the error for further handling |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const onReset = useCallback(() => { |
|
|
|
//setFilteredItems(items ?? []); |
|
|
|
setFilterObj({}); |
|
|
|
setTempSelectedValue({}); |
|
|
|
refetchData(); |
|
|
|
}, [items]); |
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
<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("Product / Material Details")} iconPosition="end"/> |
|
|
|
<Tab label={t("Qc items")} iconPosition="end" /> |
|
|
|
</Tabs> |
|
|
|
{serverError && ( |
|
|
|
<Typography variant="body2" color="error" alignSelf="flex-end"> |
|
|
|
{serverError} |
|
|
|
</Typography> |
|
|
|
)} |
|
|
|
{tabIndex === 0 && <ProductDetails />} |
|
|
|
{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") as String : t("Confirm") as String} |
|
|
|
</Button> |
|
|
|
<Button |
|
|
|
variant="outlined" |
|
|
|
startIcon={<Close />} |
|
|
|
onClick={handleCancel} |
|
|
|
> |
|
|
|
{t("Cancel") as String} |
|
|
|
</Button> |
|
|
|
</Stack> |
|
|
|
</Stack> |
|
|
|
</FormProvider> |
|
|
|
<SearchBox |
|
|
|
criteria={searchCriteria} |
|
|
|
onSearch={(query) => { |
|
|
|
setFilterObj({ |
|
|
|
...query |
|
|
|
}) |
|
|
|
}} |
|
|
|
onReset={onReset} |
|
|
|
/> |
|
|
|
<SearchResults<ItemsResult> |
|
|
|
items={filteredItems} |
|
|
|
columns={columns} |
|
|
|
setPagingController={setPagingController} |
|
|
|
pagingController={pagingController} |
|
|
|
isAutoPaging={false} |
|
|
|
/> |
|
|
|
</> |
|
|
|
); |
|
|
|
}; |
|
|
|
export default CreateItem; |
|
|
|
|
|
|
|
export default RSSOverview; |