@@ -0,0 +1,49 @@ | |||
import { TypeEnum } from "@/app/utils/typeEnum"; | |||
import RoughSchedule from "@/components/RoughSchedule"; | |||
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 RoughScheduleDetailView from "@/components/RoughScheduleDetail/RoughScheudleDetailView"; | |||
export const metadata: Metadata = { | |||
title: "Rough Scheduling Detail", | |||
}; | |||
const roughSchedulingDetail: React.FC = async () => { | |||
const project = TypeEnum.PRODUCT | |||
const { t } = await getServerI18n(project); | |||
// preloadClaims(); | |||
return ( | |||
<> | |||
<Stack | |||
direction="row" | |||
justifyContent="space-between" | |||
flexWrap="wrap" | |||
rowGap={2} | |||
> | |||
<Typography variant="h4" marginInlineEnd={2}> | |||
{t("Rough Scheduling Detail")} | |||
</Typography> | |||
{/* <Button | |||
variant="contained" | |||
startIcon={<Add />} | |||
LinkComponent={Link} | |||
href="product/create" | |||
> | |||
{t("Create product")} | |||
</Button> */} | |||
</Stack> | |||
<Suspense fallback={<RoughScheduleDetailView.Loading />}> | |||
<RoughScheduleDetailView /> | |||
</Suspense> | |||
</> | |||
); | |||
}; | |||
export default roughSchedulingDetail; |
@@ -15,6 +15,7 @@ const pathToLabelMap: { [path: string]: string } = { | |||
"/settings/qcItem": "Qc Item", | |||
"/settings/rss": "Rough Schedule Setting", | |||
"/scheduling/rough": "Rough Scheduling", | |||
"/scheduling/rough/edit": "Rough Scheduling Detail", | |||
"/scheduling/detail": "Detail Scheduling", | |||
}; | |||
@@ -0,0 +1,39 @@ | |||
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"; | |||
export const GeneralLoading: 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 GeneralLoading; |
@@ -74,12 +74,17 @@ const RSOverview: React.FC<Props> = ({ records }) => { | |||
[router] | |||
); | |||
const onDetailClick = (record: any) => { | |||
console.log("[debug] record", record); | |||
router.push(`/scheduling/rough/edit?id=${record.id}`); | |||
} | |||
const columns = useMemo<Column<ItemsResult>[]>( | |||
() => [ | |||
{ | |||
name: "id", | |||
label: t("Details"), | |||
onClick: ()=>{}, | |||
onClick: (record)=>onDetailClick(record), | |||
buttonIcon: <EditNote />, | |||
}, | |||
{ | |||
@@ -0,0 +1,101 @@ | |||
"use client"; | |||
import { | |||
Box, | |||
Card, | |||
CardContent, | |||
Grid, | |||
Stack, | |||
TextField, | |||
Typography, | |||
} from "@mui/material"; | |||
import { useFormContext } from "react-hook-form"; | |||
import { useTranslation } from "react-i18next"; | |||
import InputDataGrid from "../InputDataGrid"; | |||
import {useCallback, useEffect, 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 { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||
import {NumberInputProps} from "@/components/CreateItem/NumberInputProps"; | |||
type Props = { | |||
// isEditMode: boolean; | |||
// type: TypeEnum; | |||
}; | |||
const DetailInfoCard: React.FC<Props> = ({ recordDetails, isEditing}) => { | |||
const { | |||
t, | |||
i18n: { language }, | |||
} = useTranslation(); | |||
const { | |||
register, | |||
formState: { errors, defaultValues, touchedFields }, | |||
} = useFormContext<CreateItemInputs>(); | |||
const [details, setDetails] = useState(null); | |||
useEffect(()=>{ | |||
console.log("[debug] record details", recordDetails) | |||
setDetails(recordDetails); | |||
},[recordDetails]) | |||
useEffect(()=>{ | |||
console.log("[debug] isEdit", isEditing) | |||
},[isEditing]) | |||
return ( | |||
<Card sx={{ display: "block" }}> | |||
<CardContent component={Stack} spacing={4}> | |||
<Box> | |||
<Typography variant="overline" display="block" marginBlockEnd={1}> | |||
{t("Schedule Detail")} | |||
</Typography> | |||
<Grid container spacing={2} columns={{ xs: 6, sm: 12 }}> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("Date")} | |||
fullWidth | |||
{...register("scheduledPeriod", { | |||
required: "name required!", | |||
})} | |||
defaultValue={details?.scheduledPeriod} | |||
disabled={!isEditing} | |||
error={Boolean(errors.name)} | |||
helperText={errors.name?.message} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("Total FG Type")} | |||
fullWidth | |||
{...register("productCount", { | |||
required: "code required!", | |||
})} | |||
defaultValue={details?.productCount} | |||
disabled={!isEditing} | |||
error={Boolean(errors.code)} | |||
helperText={errors.code?.message} | |||
/> | |||
</Grid> | |||
<Grid item xs={6}> | |||
<TextField | |||
label={t("Total Estimated Production Count")} | |||
fullWidth | |||
{...register("productionCount", { | |||
required: "type required!", | |||
})} | |||
disabled={!isEditing} | |||
defaultValue={details?.productionCount} | |||
error={Boolean(errors.type)} | |||
helperText={errors.type?.message} | |||
/> | |||
</Grid> | |||
</Grid> | |||
</Box> | |||
</CardContent> | |||
</Card> | |||
); | |||
}; | |||
export default DetailInfoCard; |
@@ -0,0 +1,50 @@ | |||
import { CreateItemInputs } from "@/app/api/settings/item/actions"; | |||
import { fetchItem } from "@/app/api/settings/item"; | |||
import GeneralLoading from "@/components/General/GeneralLoading"; | |||
import RoughScheduleDetailView from "@/components/RoughScheduleDetail/RoughScheudleDetailView"; | |||
interface SubComponents { | |||
Loading: typeof GeneralLoading; | |||
} | |||
type Props = { | |||
id?: number | |||
// type: TypeEnum; | |||
}; | |||
const RoughScheduleDetailWrapper: (id: { id: any }) => Promise<JSX.Element> = async ({ id: any }) => { | |||
var result | |||
var defaultValues: Partial<CreateItemInputs> | undefined | |||
// console.log(type) | |||
var qcChecks | |||
if (id) { | |||
result = await fetchItem(id); | |||
const item = result.item | |||
qcChecks = result.qcChecks | |||
const activeRows = qcChecks.filter(it => it.isActive).map(i => i.id) | |||
console.log(qcChecks) | |||
defaultValues = { | |||
type: item?.type, | |||
id: item?.id, | |||
code: item?.code, | |||
name: item?.name, | |||
description: item?.description, | |||
remarks: item?.remarks, | |||
shelfLife: item?.shelfLife, | |||
countryOfOrigin: item?.countryOfOrigin, | |||
maxQty: item?.maxQty, | |||
qcChecks: qcChecks, | |||
qcChecks_active: activeRows | |||
}; | |||
} | |||
return ( | |||
<RoughScheduleDetailView | |||
isEditMode={Boolean(id)} | |||
defaultValues={defaultValues} | |||
qcChecks={qcChecks || []} | |||
/> | |||
); | |||
}; | |||
RoughScheduleDetailWrapper.Loading = GeneralLoading; | |||
export default RoughScheduleDetailWrapper; |
@@ -0,0 +1,199 @@ | |||
"use client"; | |||
import { useCallback, useEffect, useMemo, useState } from "react"; | |||
import { useRouter, useSearchParams } from "next/navigation"; | |||
import { useTranslation } from "react-i18next"; | |||
import { | |||
CreateItemInputs, | |||
saveItem, | |||
} from "@/app/api/settings/item/actions"; | |||
import { | |||
FormProvider, | |||
SubmitErrorHandler, | |||
SubmitHandler, | |||
useForm, | |||
} from "react-hook-form"; | |||
import { deleteDialog } from "../Swal/CustomAlerts"; | |||
import {Box, Button, Grid, Link, Stack, Tab, Tabs, TabsProps, Typography} from "@mui/material"; | |||
import {Add, Check, Close, EditNote} from "@mui/icons-material"; | |||
import {ItemQc, ItemsResult} from "@/app/api/settings/item"; | |||
import { useGridApiRef } from "@mui/x-data-grid"; | |||
import ProductDetails from "@/components/CreateItem/ProductDetails"; | |||
import QcDetails from "@/components/CreateItem/QcDetails"; | |||
import DetailInfoCard from "@/components/RoughScheduleDetail/DetailInfoCard"; | |||
type Props = { | |||
isEditMode: boolean; | |||
// type: TypeEnum; | |||
defaultValues: Partial<CreateItemInputs> | undefined; | |||
qcChecks: ItemQc[] | |||
}; | |||
const RoughScheduleDetailView: 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(""); | |||
const [tabIndex, setTabIndex] = useState(0); | |||
const { t } = useTranslation(); | |||
const router = useRouter(); | |||
const [isEdit, setIsEdit] = useState(false); | |||
//const title = "Rough Schedule Detail" | |||
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 = "Product"; | |||
redirPath = "scheduling/rough/edit"; | |||
// } | |||
// 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 handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||
(_e, newValue) => { | |||
setTabIndex(newValue); | |||
}, | |||
[], | |||
); | |||
const handleCancel = () => { | |||
router.replace(`/scheduling/rough`); | |||
}; | |||
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.")); | |||
return false; | |||
} | |||
} 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<CreateItemInputs>>( | |||
(errors) => {}, | |||
[] | |||
); | |||
const onClickEdit = () =>{ | |||
setIsEdit(!isEdit) | |||
} | |||
return ( | |||
<> | |||
<FormProvider {...formProps}> | |||
<Stack | |||
spacing={2} | |||
component="form" | |||
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | |||
> | |||
{/*<Grid>*/} | |||
{/* <Typography mb={2} variant="h4">*/} | |||
{/* {t(`${mode} ${title}`)}*/} | |||
{/* </Typography>*/} | |||
{/*</Grid>*/} | |||
<DetailInfoCard | |||
recordDetails={{ | |||
id: 1, | |||
scheduledPeriod: "2025-05-11 to 2025-05-17", | |||
scheduledAt: "2025-05-07", | |||
productCount: 13, | |||
productionCount: 21000 | |||
}} | |||
isEditing={isEdit} | |||
/> | |||
<Stack | |||
direction="row" | |||
justifyContent="space-between" | |||
flexWrap="wrap" | |||
rowGap={2} | |||
> | |||
<Button | |||
variant="contained" | |||
onClick={onClickEdit} | |||
// startIcon={<Add />} | |||
//LinkComponent={Link} | |||
//href="qcCategory/create" | |||
> | |||
{isEdit ? t("Save") : t("Edit")} | |||
</Button> | |||
</Stack> | |||
<Tabs value={tabIndex} onChange={handleTabChange} variant="scrollable"> | |||
<Tab label={t("View By Schedule")} iconPosition="end"/> | |||
<Tab label={t("View By Material")} 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") : t("Confirm")} | |||
</Button> | |||
<Button | |||
variant="outlined" | |||
startIcon={<Close />} | |||
onClick={handleCancel} | |||
> | |||
{t("Cancel")} | |||
</Button> | |||
</Stack> | |||
</Stack> | |||
</FormProvider> | |||
</> | |||
); | |||
}; | |||
export default RoughScheduleDetailView; |
@@ -0,0 +1 @@ | |||
export { default } from "./RoughScheduleDetailWrapper"; |