/* eslint-disable react-hooks/exhaustive-deps */ import React, { useCallback, useEffect, useState } from "react"; import { FormProvider, useForm } from "react-hook-form"; import { useMediaQuery } from "usehooks-ts"; import Container from "@/components/shared/container"; import { Button } from "@/components/ui/button/button"; import type { AppliedFilterData } from "@/components/widget/applied-filter/applied-filter"; import AppliedFilter from "@/components/widget/applied-filter/applied-filter"; import ListSkeleton from "@/components/widget/list-skeleton/list-skeleton"; import NoListData from "@/components/widget/no-list-data/no-list-data"; import { useLocale } from "@/hooks/useLocale"; import CoreListEmpty from "./core-list-empty"; import { CoreListFilter, type FilterItem, FilterItemTypeEnum, } from "./core-list-filter/core-list-filter"; import { CoreListFilterResponsive } from "./core-list-filter/core-list-filter-responsive"; import type { CoreListSortValueType } from "./core-list-sort"; import CoreListSort from "./core-list-sort"; export type EventPayload = { type: "filter" | "sort" | "loadMore"; payload: any; }; export type OnSubmitPayload = { type: "filter"; payload: Record; }; interface CoreListProps { data: T[]; isLoading: boolean; isEmpty: boolean; hasMore?: boolean; sortValue?: CoreListSortValueType; totalCount?: number; noListDataText?: string; noDataText?: string; hideFilter?: boolean; hiderSort?: boolean; appliedFilters?: AppliedFilterData[]; filterOptions?: FilterItem[]; filterOptionValues?: Record; onSubmit?: (payload: OnSubmitPayload["payload"]) => void; onChange?: (eventPayload: EventPayload) => void; onClearFilter?: () => void; onRemoveFilter?: (item: AppliedFilterData) => void; renderItem: (item: T) => React.ReactNode; renderHeader?: (item: T) => React.ReactNode; } const getPriceRangeFieldNames = (filterOptions: FilterItem[]) => filterOptions .filter((item) => item.type === FilterItemTypeEnum.PRICE_RANGE) .map((item) => item.name); const CoreList = (args: CoreListProps) => { const { t } = useLocale(); const { filterOptions = [], ...props } = args; const isDesktop = useMediaQuery("(min-width: 768px)"); const methods = useForm({ defaultValues: props.filterOptionValues, }); const [mobileSortOpen, setMobileSortOpen] = useState(false); const [mobileFilterOpen, setMobileFilterOpen] = useState(false); const handleSubmit = useCallback( (val: any, shouldSubmit?: boolean) => { if (isDesktop || shouldSubmit) props?.onSubmit?.(val); }, [isDesktop, props], ); const priceRangeNames = getPriceRangeFieldNames(filterOptions); const priceRangeValues = methods.watch(priceRangeNames); /** * This useEffect for handling the total price filter */ useEffect(() => { const submitable = priceRangeValues.some((item) => !!item); if (submitable) { handleSubmit(methods.getValues()); } }, priceRangeValues); if (props.isLoading) return (
); const handleFilterChange = (eventPayload: EventPayload) => { switch (eventPayload.type) { case "loadMore": props?.onChange?.(eventPayload); break; case "sort": props?.onChange?.(eventPayload); break; default: break; } }; const handleLoadMore = () => { handleFilterChange({ type: "loadMore", payload: {}, }); }; const handleMobileModalClose = () => { setMobileFilterOpen(false); }; const handleMobileModalOpen = () => { methods.reset(props.filterOptionValues); setMobileFilterOpen(true); }; const handleOnRemoveFilter = (item: AppliedFilterData) => { props?.onRemoveFilter?.(item); const fieldValues = methods.getValues(item.filterKey); methods.setValue( item.filterKey, fieldValues.filter((val: any) => val !== item.filterValue), ); }; /** * This function clears all filters without price range fields * This case came from the test unit. */ const handleClearFilter = () => { props?.onClearFilter?.(); const formValues = methods.getValues(); const keysWithoutPriceRangeFields = Object.keys(formValues).filter( (key) => !priceRangeNames.includes(key), ); keysWithoutPriceRangeFields.forEach((key) => { methods.resetField(key); }); }; if (props.isEmpty) return (
handleSubmit(vals))} onChange={() => handleSubmit(methods.getValues())} >
); return (
handleSubmit(vals))} onChange={() => handleSubmit(methods.getValues())} >
{isDesktop && !props?.hideFilter && (
)}
{props.data && props.data.length > 0 && (
{props.renderHeader?.(props.data[0]!)}
)}
{!props?.hideFilter && !isDesktop && ( handleSubmit(methods.getValues(), true)} > )} {!props.hiderSort && ( setMobileSortOpen(false)} onMobileModalOpen={() => setMobileSortOpen(true)} mobileModalOpen={mobileSortOpen} value={props?.sortValue || "Average"} onChange={(value) => { setMobileSortOpen(false); handleFilterChange({ type: "sort", payload: { name: "price", value, }, }); }} /> )}
{props.totalCount === 0 ? ( ) : ( <>
{props.data.map((item) => (
{props.renderItem(item)}
))}
{t("You have viewed {{number}} of {{count}} results", { number: props.data.length, count: props.totalCount, })} {props.hasMore && ( )}
)}
); }; export default CoreList;