@@ -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/qcItem": "Qc Item", | ||||
"/settings/rss": "Rough Schedule Setting", | "/settings/rss": "Rough Schedule Setting", | ||||
"/scheduling/rough": "Rough Scheduling", | "/scheduling/rough": "Rough Scheduling", | ||||
"/scheduling/rough/edit": "Rough Scheduling Detail", | |||||
"/scheduling/detail": "Detail Scheduling", | "/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] | [router] | ||||
); | ); | ||||
const onDetailClick = (record: any) => { | |||||
console.log("[debug] record", record); | |||||
router.push(`/scheduling/rough/edit?id=${record.id}`); | |||||
} | |||||
const columns = useMemo<Column<ItemsResult>[]>( | const columns = useMemo<Column<ItemsResult>[]>( | ||||
() => [ | () => [ | ||||
{ | { | ||||
name: "id", | name: "id", | ||||
label: t("Details"), | label: t("Details"), | ||||
onClick: ()=>{}, | |||||
onClick: (record)=>onDetailClick(record), | |||||
buttonIcon: <EditNote />, | 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"; |