|
|
@@ -1,6 +1,6 @@ |
|
|
|
"use client"; |
|
|
|
|
|
|
|
import { DoResult } from "@/app/api/do" |
|
|
|
import { DoResult } from "@/app/api/do"; |
|
|
|
import { useRouter } from "next/navigation"; |
|
|
|
import { useCallback, useMemo, useState } from "react"; |
|
|
|
import { useTranslation } from "react-i18next"; |
|
|
@@ -11,105 +11,289 @@ import { arrayToDateString, arrayToDayjs } from "@/app/utils/formatUtil"; |
|
|
|
import SearchBox from "../SearchBox/SearchBox"; |
|
|
|
import SearchResults from "../SearchResults/SearchResults"; |
|
|
|
import { EditNote } from "@mui/icons-material"; |
|
|
|
import InputDataGrid from "../InputDataGrid"; |
|
|
|
import { CreateConsoDoInput } from "@/app/api/do/actions"; |
|
|
|
import { TableRow } from "../InputDataGrid/InputDataGrid"; |
|
|
|
import { FooterPropsOverrides, GridColDef, GridRowModel, GridToolbarContainer, useGridApiRef } from "@mui/x-data-grid"; |
|
|
|
import { |
|
|
|
FormProvider, |
|
|
|
SubmitErrorHandler, |
|
|
|
SubmitHandler, |
|
|
|
useForm, |
|
|
|
} from "react-hook-form"; |
|
|
|
import { Box, Button, Grid, Stack, Typography } from "@mui/material"; |
|
|
|
import StyledDataGrid from "../StyledDataGrid"; |
|
|
|
import { GridRowSelectionModel } from "@mui/x-data-grid"; |
|
|
|
|
|
|
|
type Props = { |
|
|
|
dos: DoResult[] |
|
|
|
} |
|
|
|
dos: DoResult[]; |
|
|
|
}; |
|
|
|
|
|
|
|
type SearchQuery = Partial<Omit<DoResult, "id">>; |
|
|
|
type SearchParamNames = keyof SearchQuery; |
|
|
|
// put all this into a new component |
|
|
|
// ConsoDoForm |
|
|
|
type EntryError = |
|
|
|
| { |
|
|
|
[field in keyof DoResult]?: string; |
|
|
|
} |
|
|
|
| undefined; |
|
|
|
type DoRow = TableRow<Partial<DoResult>, EntryError>; |
|
|
|
|
|
|
|
const DoSearch: React.FC<Props> = ({ |
|
|
|
dos |
|
|
|
}) => { |
|
|
|
const DoSearch: React.FC<Props> = ({ dos }) => { |
|
|
|
const apiRef = useGridApiRef(); |
|
|
|
|
|
|
|
const [filteredDos, setFilteredDos] = useState<DoResult[]>(dos); |
|
|
|
const { t } = useTranslation("do"); |
|
|
|
const router = useRouter(); |
|
|
|
const formProps = useForm<CreateConsoDoInput>({ |
|
|
|
defaultValues: {}, |
|
|
|
}); |
|
|
|
const errors = formProps.formState.errors; |
|
|
|
|
|
|
|
const searchCriteria: Criterion<SearchParamNames>[] = useMemo(() => [ |
|
|
|
{ label: t("Code"), paramName: "code", type: "text" }, |
|
|
|
|
|
|
|
{ label: t("Order Date From"), label2: t("Order Date To"), paramName: "orderDate", type: "dateRange" }, |
|
|
|
{ label: t("Shop Name"), paramName: "shopName", type: "text" }, |
|
|
|
{ |
|
|
|
label: t("Status"), paramName: "status", type: "autocomplete", options: sortBy( |
|
|
|
uniqBy(dos.map((_do) => ({ value: _do.status, label: t(upperFirst(_do.status)) })), "value"), |
|
|
|
"label") |
|
|
|
}, |
|
|
|
], [t, dos]) |
|
|
|
const tttt = [ |
|
|
|
{ |
|
|
|
id: 1, |
|
|
|
code: "string", |
|
|
|
orderDate: "2025-01-01", |
|
|
|
estimatedArrivalDate: "2025-01-01", |
|
|
|
status: "string", |
|
|
|
shopName: "string", |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 2, |
|
|
|
code: "string1", |
|
|
|
orderDate: "2025-01-01", |
|
|
|
estimatedArrivalDate: "2025-01-01", |
|
|
|
status: "string", |
|
|
|
shopName: "string", |
|
|
|
}, |
|
|
|
{ |
|
|
|
id: 3, |
|
|
|
code: "string2", |
|
|
|
orderDate: "2025-01-01", |
|
|
|
estimatedArrivalDate: "2025-01-01", |
|
|
|
status: "string", |
|
|
|
shopName: "string", |
|
|
|
}, |
|
|
|
] |
|
|
|
// const [filteredDos, setFilteredDos] = useState<DoResult[]>(dos); |
|
|
|
const [filteredDos, setFilteredDos] = useState<DoResult[]>(tttt); |
|
|
|
const { t } = useTranslation("do"); |
|
|
|
const router = useRouter(); |
|
|
|
const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>([]) |
|
|
|
|
|
|
|
const onReset = useCallback(() => { |
|
|
|
setFilteredDos(dos) |
|
|
|
}, [dos]) |
|
|
|
const searchCriteria: Criterion<SearchParamNames>[] = useMemo( |
|
|
|
() => [ |
|
|
|
{ label: t("Code"), paramName: "code", type: "text" }, |
|
|
|
|
|
|
|
const onDetailClick = useCallback( |
|
|
|
(doResult: DoResult) => { |
|
|
|
router.push(`/do/edit?id=${doResult.id}`); |
|
|
|
}, |
|
|
|
[router] |
|
|
|
); |
|
|
|
{ |
|
|
|
label: t("Order Date From"), |
|
|
|
label2: t("Order Date To"), |
|
|
|
paramName: "orderDate", |
|
|
|
type: "dateRange", |
|
|
|
}, |
|
|
|
{ label: t("Shop Name"), paramName: "shopName", type: "text" }, |
|
|
|
{ |
|
|
|
label: t("Status"), |
|
|
|
paramName: "status", |
|
|
|
type: "autocomplete", |
|
|
|
options: sortBy( |
|
|
|
uniqBy( |
|
|
|
dos.map((_do) => ({ |
|
|
|
value: _do.status, |
|
|
|
label: t(upperFirst(_do.status)), |
|
|
|
})), |
|
|
|
"value" |
|
|
|
), |
|
|
|
"label" |
|
|
|
), |
|
|
|
}, |
|
|
|
], |
|
|
|
[t, dos] |
|
|
|
); |
|
|
|
|
|
|
|
const onReset = useCallback(() => { |
|
|
|
setFilteredDos(dos); |
|
|
|
}, [dos]); |
|
|
|
|
|
|
|
const columns = useMemo<Column<DoResult>[]>(() => [ |
|
|
|
{ |
|
|
|
name: "id", |
|
|
|
label: t("Details"), |
|
|
|
onClick: onDetailClick, |
|
|
|
buttonIcon: <EditNote />, |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "code", |
|
|
|
label: t("Code") |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "shopName", |
|
|
|
label: t("Shop Name") |
|
|
|
const onDetailClick = useCallback( |
|
|
|
(doResult: DoResult) => { |
|
|
|
router.push(`/do/edit?id=${doResult.id}`); |
|
|
|
}, |
|
|
|
[router] |
|
|
|
); |
|
|
|
|
|
|
|
const validationTest = useCallback( |
|
|
|
( |
|
|
|
newRow: GridRowModel<DoRow> |
|
|
|
// rowModel: GridRowSelectionModel |
|
|
|
): EntryError => { |
|
|
|
const error: EntryError = {}; |
|
|
|
console.log(newRow); |
|
|
|
// if (!newRow.lowerLimit) { |
|
|
|
// error["lowerLimit"] = "lower limit cannot be null" |
|
|
|
// } |
|
|
|
// if (newRow.lowerLimit && newRow.upperLimit && newRow.lowerLimit > newRow.upperLimit) { |
|
|
|
// error["lowerLimit"] = "lower limit should not be greater than upper limit" |
|
|
|
// error["upperLimit"] = "lower limit should not be greater than upper limit" |
|
|
|
// } |
|
|
|
return Object.keys(error).length > 0 ? error : undefined; |
|
|
|
}, |
|
|
|
[] |
|
|
|
); |
|
|
|
|
|
|
|
const columns = useMemo<GridColDef[]>( |
|
|
|
() => [ |
|
|
|
// { |
|
|
|
// name: "id", |
|
|
|
// label: t("Details"), |
|
|
|
// onClick: onDetailClick, |
|
|
|
// buttonIcon: <EditNote />, |
|
|
|
// }, |
|
|
|
{ |
|
|
|
field: "code", |
|
|
|
headerName: "code", |
|
|
|
flex: 1, |
|
|
|
}, |
|
|
|
{ |
|
|
|
field: "shopName", |
|
|
|
headerName: t("Shop Name"), |
|
|
|
flex: 1, |
|
|
|
}, |
|
|
|
{ |
|
|
|
field: "orderDate", |
|
|
|
headerName: t("Order Date"), |
|
|
|
flex: 1, |
|
|
|
renderCell: (params) => { |
|
|
|
return params.row.orderDate |
|
|
|
? arrayToDateString(params.row.orderDate) |
|
|
|
: "N/A"; |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "orderDate", |
|
|
|
label: t("Order Date"), |
|
|
|
renderCell: (params) => { |
|
|
|
return params.orderDate ? arrayToDateString(params.orderDate) : "N/A" |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
field: "estimatedArrivalDate", |
|
|
|
headerName: t("Estimated Arrival"), |
|
|
|
flex: 1, |
|
|
|
renderCell: (params) => { |
|
|
|
return params.row.estimatedArrivalDate |
|
|
|
? arrayToDateString(params.row.estimatedArrivalDate) |
|
|
|
: "N/A"; |
|
|
|
}, |
|
|
|
{ |
|
|
|
name: "status", |
|
|
|
label: t("Status"), |
|
|
|
renderCell: (params) => { |
|
|
|
return t(upperFirst(params.status)) |
|
|
|
}, |
|
|
|
}, |
|
|
|
{ |
|
|
|
field: "status", |
|
|
|
headerName: t("Status"), |
|
|
|
flex: 1, |
|
|
|
renderCell: (params) => { |
|
|
|
return t(upperFirst(params.row.status)); |
|
|
|
}, |
|
|
|
], [t]) |
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
<SearchBox |
|
|
|
criteria={searchCriteria} |
|
|
|
onSearch={(query) => { |
|
|
|
setFilteredDos( |
|
|
|
dos.filter( |
|
|
|
(_do) => { |
|
|
|
const doOrderDateStr = arrayToDayjs(_do.orderDate) |
|
|
|
}, |
|
|
|
], |
|
|
|
[t, arrayToDateString] |
|
|
|
); |
|
|
|
|
|
|
|
return _do.code.toLowerCase().includes(query.code.toLowerCase()) |
|
|
|
&& _do.shopName.toLowerCase().includes(query.shopName.toLowerCase()) |
|
|
|
&& (query.status == "All" || _do.status.toLowerCase().includes(query.status.toLowerCase())) |
|
|
|
&& (isEmpty(query.orderDate) || doOrderDateStr.isSame(query.orderDate) || doOrderDateStr.isAfter(query.orderDate)) |
|
|
|
&& (isEmpty(query.orderDateTo) || doOrderDateStr.isSame(query.orderDateTo) || doOrderDateStr.isBefore(query.orderDateTo)) |
|
|
|
} |
|
|
|
) |
|
|
|
) |
|
|
|
}} |
|
|
|
onReset={onReset} |
|
|
|
/> |
|
|
|
<SearchResults<DoResult> items={filteredDos} columns={columns} pagingController={{ |
|
|
|
pageNum: 0, |
|
|
|
pageSize: 0, |
|
|
|
totalCount: 0, |
|
|
|
}} /> |
|
|
|
</> |
|
|
|
) |
|
|
|
} |
|
|
|
const onSubmit = useCallback<SubmitHandler<CreateConsoDoInput>>( |
|
|
|
async (data, event) => { |
|
|
|
let hasErrors = false; |
|
|
|
console.log(errors); |
|
|
|
console.log(data); |
|
|
|
}, |
|
|
|
[] |
|
|
|
); |
|
|
|
const onSubmitError = useCallback<SubmitErrorHandler<CreateConsoDoInput>>( |
|
|
|
(errors) => {}, |
|
|
|
[] |
|
|
|
); |
|
|
|
return ( |
|
|
|
<> |
|
|
|
<FormProvider {...formProps}> |
|
|
|
<Stack |
|
|
|
spacing={2} |
|
|
|
component="form" |
|
|
|
onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} |
|
|
|
> |
|
|
|
<Grid container> |
|
|
|
<Grid item xs={8}> |
|
|
|
<Typography variant="h4" marginInlineEnd={2}> |
|
|
|
{t("Delivery Order")} |
|
|
|
</Typography> |
|
|
|
</Grid> |
|
|
|
<Grid |
|
|
|
item |
|
|
|
xs={4} |
|
|
|
display="flex" |
|
|
|
justifyContent="end" |
|
|
|
alignItems="end" |
|
|
|
> |
|
|
|
<Button |
|
|
|
name="submit" |
|
|
|
variant="contained" |
|
|
|
// startIcon={<Check />} |
|
|
|
type="submit" |
|
|
|
> |
|
|
|
{t("Create")} |
|
|
|
</Button> |
|
|
|
</Grid> |
|
|
|
</Grid> |
|
|
|
<SearchBox |
|
|
|
criteria={searchCriteria} |
|
|
|
onSearch={(query) => { |
|
|
|
setFilteredDos( |
|
|
|
dos.filter((_do) => { |
|
|
|
const doOrderDateStr = arrayToDayjs(_do.orderDate); |
|
|
|
return ( |
|
|
|
_do.code.toLowerCase().includes(query.code.toLowerCase()) && |
|
|
|
_do.shopName |
|
|
|
.toLowerCase() |
|
|
|
.includes(query.shopName.toLowerCase()) && |
|
|
|
(query.status == "All" || |
|
|
|
_do.status |
|
|
|
.toLowerCase() |
|
|
|
.includes(query.status.toLowerCase())) && |
|
|
|
(isEmpty(query.orderDate) || |
|
|
|
doOrderDateStr.isSame(query.orderDate) || |
|
|
|
doOrderDateStr.isAfter(query.orderDate)) && |
|
|
|
(isEmpty(query.orderDateTo) || |
|
|
|
doOrderDateStr.isSame(query.orderDateTo) || |
|
|
|
doOrderDateStr.isBefore(query.orderDateTo)) |
|
|
|
); |
|
|
|
}) |
|
|
|
); |
|
|
|
}} |
|
|
|
onReset={onReset} |
|
|
|
/> |
|
|
|
<StyledDataGrid |
|
|
|
checkboxSelection={true} |
|
|
|
rows={filteredDos} |
|
|
|
columns={columns} |
|
|
|
rowSelectionModel={rowSelectionModel} |
|
|
|
onRowSelectionModelChange={(newRowSelectionModel) => { |
|
|
|
setRowSelectionModel(newRowSelectionModel); |
|
|
|
formProps.setValue("ids", newRowSelectionModel) |
|
|
|
}} |
|
|
|
slots={{ |
|
|
|
footer: FooterToolbar, |
|
|
|
noRowsOverlay: NoRowsOverlay, |
|
|
|
}} |
|
|
|
/> |
|
|
|
</Stack> |
|
|
|
</FormProvider> |
|
|
|
</> |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
export default DoSearch; |
|
|
|
const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => { |
|
|
|
return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>; |
|
|
|
}; |
|
|
|
const NoRowsOverlay: React.FC = () => { |
|
|
|
const { t } = useTranslation("home"); |
|
|
|
return ( |
|
|
|
<Box |
|
|
|
display="flex" |
|
|
|
justifyContent="center" |
|
|
|
alignItems="center" |
|
|
|
height="100%" |
|
|
|
> |
|
|
|
<Typography variant="caption">{t("Add some entries!")}</Typography> |
|
|
|
</Box> |
|
|
|
); |
|
|
|
}; |
|
|
|
export default DoSearch; |