diff --git a/resources/js/Components/DatepickerFilter.tsx b/resources/js/Components/DatepickerFilter.tsx new file mode 100644 index 0000000..f3f1198 --- /dev/null +++ b/resources/js/Components/DatepickerFilter.tsx @@ -0,0 +1,59 @@ +import * as React from "react"; +import { FormProvider, useForm, useWatch } from "react-hook-form"; +import RangeDatePicker from "./Forms/RangeDatepicker"; + +export type PopupFilterProps = { + date: Date[]; + setDate: React.Dispatch>; + onResetFilter?: () => void; +} & React.ComponentPropsWithoutRef<"div">; + +type FormData = { + filter: Date[]; +}; + +export default function DatepickerFilter({ + date, + setDate, + onResetFilter, +}: PopupFilterProps) { + //#region //*=========== Form ===========t + const methods = useForm({ + mode: "onTouched", + defaultValues: { + filter: date, + }, + }); + const { control, setValue } = methods; + + const filter = + useWatch({ + control, + name: "filter", + }) ?? []; + //#endregion //*======== Form =========== + + React.useEffect(() => { + setDate(filter); + }, [filter]); + + const resetFilter = () => { + onResetFilter?.(); + setValue("filter", []); + }; + + return ( + +
+ 0} + onClearDate={resetFilter} + /> +
+
+ ); +} diff --git a/resources/js/Components/Forms/RangeDatepicker.tsx b/resources/js/Components/Forms/RangeDatepicker.tsx index b4b06be..1bf1df6 100644 --- a/resources/js/Components/Forms/RangeDatepicker.tsx +++ b/resources/js/Components/Forms/RangeDatepicker.tsx @@ -1,6 +1,6 @@ import clsx from "clsx"; import get from "lodash.get"; -import { Calendar } from "lucide-react"; +import { Calendar, XCircle } from "lucide-react"; import { Controller, RegisterOptions, useFormContext } from "react-hook-form"; import "react-datepicker/dist/react-datepicker.css"; @@ -16,12 +16,12 @@ type ReactDatePickerProps = { placeholder?: string; defaultYear?: number; defaultMonth?: number; - defaultValue?: string; helperText?: string; readOnly?: boolean; /** Disable error style (not disabling error validation) */ hideError?: boolean; containerClassName?: string; + onClearDate?: () => void; } & Omit; export default function RangeDatePicker({ @@ -31,11 +31,12 @@ export default function RangeDatePicker({ placeholder, defaultYear, defaultMonth, - defaultValue, helperText, readOnly = false, hideError = false, disabled, + isClearable, + onClearDate, containerClassName, }: ReactDatePickerProps) { const { @@ -76,7 +77,6 @@ export default function RangeDatePicker({ { const startDate = Array.isArray(value) @@ -100,7 +100,7 @@ export default function RangeDatePicker({ endDate={endDate} className={clsx( "flex w-full rounded-lg shadow-sm", - "min-h-[2.25rem] py-0 md:min-h-[2.5rem]", + "min-h-[2.5rem] py-0 md:min-h-[2.75rem]", "border-gray-300 focus:border-primary-500 focus:ring-primary-500", (readOnly || disabled) && "cursor-not-allowed border-gray-300 bg-gray-100 focus:border-gray-300 focus:ring-0", @@ -118,10 +118,14 @@ export default function RangeDatePicker({ disabled={disabled} selectsRange /> - +
+ {isClearable && ( + + )} + +
{helperText && ( diff --git a/resources/js/Pages/Product/Components/PopupFilter.tsx b/resources/js/Components/PopupFilter.tsx similarity index 100% rename from resources/js/Pages/Product/Components/PopupFilter.tsx rename to resources/js/Components/PopupFilter.tsx diff --git a/resources/js/Pages/Product/Index.tsx b/resources/js/Pages/Product/Index.tsx index 452f218..b29f3c5 100644 --- a/resources/js/Pages/Product/Index.tsx +++ b/resources/js/Pages/Product/Index.tsx @@ -9,7 +9,7 @@ import { Link, usePage } from "@inertiajs/react"; import { Head } from "@inertiajs/react"; import { MapPin, Plus, Search, XCircle } from "lucide-react"; import * as React from "react"; -import PopupFilter, { PopupFilterProps } from "./Components/PopupFilter"; +import PopupFilter, { PopupFilterProps } from "../../Components/PopupFilter"; type CategoryFilter = { category: string[]; diff --git a/resources/js/Pages/Transaction/Index.tsx b/resources/js/Pages/Transaction/Index.tsx index 1f61a86..19f11b8 100644 --- a/resources/js/Pages/Transaction/Index.tsx +++ b/resources/js/Pages/Transaction/Index.tsx @@ -5,6 +5,8 @@ import { AccordionTrigger, } from "@/Components/Accordion"; import Badge from "@/Components/Badge"; +import DatepickerFilter from "@/Components/DatepickerFilter"; +import PopupFilter, { PopupFilterProps } from "@/Components/PopupFilter"; import Typography from "@/Components/Typography"; import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout"; import { cn, numberToCurrency } from "@/Lib/utils"; @@ -13,12 +15,9 @@ import { CategoryType } from "@/types/entities/category"; import { TransactionType } from "@/types/entities/transaction"; import { Head } from "@inertiajs/react"; import { router } from "@inertiajs/react"; -import { format } from "date-fns"; +import { format, formatDate } from "date-fns"; import { Search, XCircle } from "lucide-react"; import * as React from "react"; -import PopupFilter, { - PopupFilterProps, -} from "../Product/Components/PopupFilter"; import NotFound from "./container/NotFound"; type CategoryFilter = { @@ -29,20 +28,36 @@ type TransactionIndexProps = { transaction: TransactionType[]; category: CategoryType[]; categoryIds: string[]; + startDate: string; + endDate: string; +}; + +type QueryType = { + category: string[]; + start_date?: string; + end_date?: string; }; export default function TransactionIndex({ transaction, category, categoryIds, + startDate, + endDate, }: TransactionIndexProps) { const [filter, setFilter] = React.useState(""); - + const [filterDate, setFilterDate] = React.useState( + [ + startDate ? new Date(startDate) : null, + endDate ? new Date(endDate) : null, + ].filter(Boolean) as Date[], + ); const [filterQuery, setFilterQuery] = React.useState({ category: categoryIds, }); const prevCategory = React.useRef(categoryIds); + const prevDate = React.useRef(filterDate); const filterOption: PopupFilterProps["filterOption"] = React.useMemo( @@ -79,68 +94,78 @@ export default function TransactionIndex({ React.useEffect(() => { if ( JSON.stringify(filterQuery.category) !== - JSON.stringify(prevCategory.current) + JSON.stringify(prevCategory.current) || + (JSON.stringify(filterDate) !== JSON.stringify(prevDate.current) && + !(!filterDate[0] !== !filterDate[1])) ) { - router.get( - "/history", - { category_id: filterQuery.category }, - { - preserveState: true, - preserveScroll: true, - }, - ); + const query: QueryType = { + category: filterQuery.category, + }; + if (filterDate[0] && filterDate[1]) { + query.start_date = formatDate(filterDate[0], "yyyy-MM-dd"); + query.end_date = formatDate(filterDate[1], "yyyy-MM-dd"); + } + router.get("/history", query, { + preserveState: true, + preserveScroll: true, + }); prevCategory.current = filterQuery.category; + prevDate.current = filterDate; } - }, [filterQuery.category]); + }, [filterQuery.category, filterDate]); return ( - {transaction.length === 0 ? ( - - ) : ( -
- - Order List - - -
-
-
- -
- - {filter !== "" && ( -
- -
- )} -
-
- setFilterQuery({ category: [] })} - /> +
+ + Order List + +
+
+
+
+ + {filter !== "" && ( +
+ +
+ )}
+
+ setFilterDate([])} + /> + setFilterQuery({ category: [] })} + /> +
+
+ {filteredTransaction.length === 0 ? ( + + ) : (
{filteredTransaction.map((t) => (
))}
-
- )} + )} +
); } diff --git a/resources/js/Pages/Transaction/container/NotFound.tsx b/resources/js/Pages/Transaction/container/NotFound.tsx index f29c5c1..5ce2252 100644 --- a/resources/js/Pages/Transaction/container/NotFound.tsx +++ b/resources/js/Pages/Transaction/container/NotFound.tsx @@ -3,7 +3,7 @@ import Typography from "@/Components/Typography"; export default function NotFound() { return ( -
+