All files / packages/app/features/receipts filter-button.tsx

89.65% Statements 26/29
86.66% Branches 13/15
92.85% Functions 13/14
92.3% Lines 24/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117                                    30x       530x   530x   530x   2x 2x       530x 530x           530x   2x   1x 1x   1x 1x               530x   2x       530x                                           246x           31x 31x                     2x     2x     24x   12x            
import React from "react";
 
import { useTranslation } from "react-i18next";
 
import { useBooleanState } from "~app/hooks/use-boolean-state";
import type { SearchParamState } from "~app/utils/navigation";
import { Button } from "~components/button";
import { Divider } from "~components/divider";
import { Icon } from "~components/icons";
import { Modal } from "~components/modal";
import { Select } from "~components/select";
import { Text } from "~components/text";
 
type Props = {
	sortState: SearchParamState<"/_protected/receipts/", "sort">;
	filtersState: SearchParamState<"/_protected/receipts/", "filters">;
};
 
export const FilterButton: React.FC<Props> = ({
	sortState: [sort, setSort],
	filtersState: [filters, setFilters],
}) => {
	const { t } = useTranslation("receipts");
	const [filterModalOpen, { switchValue: switchFilterModal }] =
		useBooleanState(false);
 
	const sortSelectOnPress = React.useCallback(
		() =>
			setSort((prevSort) =>
				prevSort === "date-desc" ? "date-asc" : "date-desc",
			),
		[setSort],
	);
	const sortIconName = sort === "date-desc" ? "sort-down" : "sort-up";
	const filterOwnerOptions: Record<"true" | "false" | "undefined", string> = {
		undefined: t("list.filters.ownership.ownedByAnybody"),
		true: t("list.filters.ownership.ownedByMe"),
		false: t("list.filters.ownership.ownedNotByMe"),
	};
 
	const onFilterSelectionChange = React.useCallback(
		(filterKey: keyof typeof filters, key: keyof typeof filterOwnerOptions) => {
			switch (key) {
				case "true":
					setFilters((prev) => ({ ...prev, [filterKey]: true }));
					break;
				case "false":
					setFilters((prev) => ({ ...prev, [filterKey]: false }));
					break;
				default:
					setFilters((prev) => ({ ...prev, [filterKey]: undefined }));
			}
		},
		[setFilters],
	);
 
	const onOwnedFilterSelectionChange = React.useCallback(
		(selection: keyof typeof filterOwnerOptions) =>
			onFilterSelectionChange("ownedByMe", selection),
		[onFilterSelectionChange],
	);
 
	return (
		<>
			<Button color="primary" isIconOnly onPress={switchFilterModal}>
				<Icon name="filter" className="size-6" />
			</Button>
			<Modal
				isOpen={filterModalOpen}
				onOpenChange={switchFilterModal}
				bodyClassName="items-center"
			>
				<Button
					variant="light"
					onPress={sortSelectOnPress}
					startContent={<Icon name={sortIconName} className="size-6" />}
				>
					{sort === "date-desc"
						? t("list.sorting.newestFirst")
						: t("list.sorting.oldestFirst")}
				</Button>
				<Divider />
				<Text className="text-2xl font-medium">{t("list.filters.title")}</Text>
				<Select
					items={(["true", "false", "undefined"] as const).map((position) => ({
						position,
					}))}
					label={t("list.filters.ownership.label")}
					placeholder=""
					renderValue={(values) =>
						values.map(({ position }) => (
							<Text key={position}>{filterOwnerOptions[position]}</Text>
						))
					}
					selectedKeys={[
						filters.ownedByMe === undefined
							? "undefined"
							: filters.ownedByMe
								? "true"
								: "false",
					]}
					onSelectionChange={(nextKeys) => {
						Iif (!nextKeys[0]) {
							return;
						}
						onOwnedFilterSelectionChange(nextKeys[0]);
					}}
					disallowEmptySelection
					getKey={({ position }) => position}
				>
					{({ position }) => <Text>{filterOwnerOptions[position]}</Text>}
				</Select>
			</Modal>
		</>
	);
};