Add option to create zone directly to filter select

This commit is contained in:
Timothy Armes
2024-04-14 15:30:34 +02:00
parent d44110649b
commit a422b1441a
6 changed files with 82 additions and 25 deletions
@@ -44,6 +44,7 @@ const ClubTable = () => {
<div className="z-10 flex w-full flex-wrap items-center justify-between gap-3 p-3"> <div className="z-10 flex w-full flex-wrap items-center justify-between gap-3 p-3">
<SearchBar /> <SearchBar />
<RegionSelect <RegionSelect
syncTitle={t("syncWithMapOption")}
classNameOverrides={{ classNameOverrides={{
container: () => "flex flex-1", container: () => "flex flex-1",
}} }}
@@ -103,7 +103,7 @@ const TournamentTable = () => {
<TimeControlFilters /> <TimeControlFilters />
</div> </div>
<RegionSelect /> <RegionSelect syncTitle={t("syncWithMapOption")} />
</div> </div>
<div className="flex justify-center" ref={datePickerRef}> <div className="flex justify-center" ref={datePickerRef}>
+66 -16
View File
@@ -1,23 +1,34 @@
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { SingleValue } from "react-select"; import { IoAdd } from "react-icons/io5";
import { GroupBase, OptionsOrGroups, SingleValue } from "react-select";
import { regionFilterAtom } from "@/atoms"; import { regionFilterAtom } from "@/atoms";
import { BaseOption, Select, SelectProps } from "@/components/form/Select"; import { BaseOption, Select, SelectProps } from "@/components/form/Select";
import { useZones } from "@/hooks/useZones"; import { useZones } from "@/hooks/useZones";
import { Zone } from "@/server/myZones"; import { Zone } from "@/server/myZones";
import { useRouter } from "@/utils/navigation";
type RegionSelectProps = Omit<SelectProps, "options" | "value" | "onChange">; type RegionSelectProps = Omit<SelectProps, "options" | "value" | "onChange"> & {
syncTitle: string;
};
export const RegionSelect = (selectProps: RegionSelectProps) => { type RegionOption = BaseOption<string, Zone | null>;
const t = useTranslations("Tournaments"); type GroupedOption = GroupBase<RegionOption>;
export const RegionSelect = ({
syncTitle,
...selectProps
}: RegionSelectProps) => {
const t = useTranslations("Zones");
const router = useRouter();
const [regionFilter, setRegionFilter] = useAtom(regionFilterAtom); const [regionFilter, setRegionFilter] = useAtom(regionFilterAtom);
const { zones } = useZones(); const { zones } = useZones();
const zoneFilterOptions: BaseOption<string, Zone | null>[] = [ const zoneFilterOptions: OptionsOrGroups<RegionOption, GroupedOption> = [
{ {
value: "map", value: "map",
label: t("syncWithMapOption"), label: syncTitle,
data: null, data: null,
}, },
{ {
@@ -26,32 +37,71 @@ export const RegionSelect = (selectProps: RegionSelectProps) => {
data: null, data: null,
}, },
...zones.map((zone) => ({ {
value: zone.id, label: t("yourZonesSelectGroupHeader"),
label: zone.name, options: [
data: zone, ...zones.map((zone) => ({
})), value: zone.id,
label: zone.name,
data: zone,
})),
{
value: "create",
label: t("createZoneSelectOption"),
data: null,
},
],
},
]; ];
const onChange = (option: SingleValue<BaseOption<string, Zone | null>>) => { const onChange = (option: SingleValue<RegionOption>) => {
if (!option) return; if (!option) return;
if (option.value === "create") {
router.push("/zones/create");
return;
}
if (option.value === "map" || option.value === "all") if (option.value === "map" || option.value === "all")
setRegionFilter(option.value); setRegionFilter(option.value);
else setRegionFilter(option.data!); else setRegionFilter(option.data!);
}; };
const formatGroupLabel = (data: GroupedOption) => (
<div className="relative -translate-y-1/2 border-b-2 border-primary-500 text-xs font-bold uppercase text-primary-500 ">
<div className="relative top-1/2 inline-block translate-y-1/2 bg-gray-50 pr-2 dark:bg-gray-700 ">
{data.label}
</div>
</div>
);
const formatOptionLabel = (option: RegionOption) => {
if (option.value === "create")
return (
<div className="flex items-center gap-1 italic">
<IoAdd />
{option.label}
</div>
);
return option.label;
};
const allOptions = zoneFilterOptions.flatMap((groupOrOption) =>
"options" in groupOrOption ? groupOrOption.options : groupOrOption,
);
return ( return (
<Select <Select
options={zoneFilterOptions} options={zoneFilterOptions}
value={zoneFilterOptions.find((o) => value={allOptions.find((o) =>
regionFilter === "all" || regionFilter === "map" regionFilter === "all" || regionFilter === "map"
? o.value === regionFilter ? o.value === regionFilter
: o.value === regionFilter.id, : o.value === regionFilter.id,
)} )}
onChange={(v) => formatGroupLabel={(g) => formatGroupLabel(g as GroupedOption)}
onChange(v as SingleValue<BaseOption<string, Zone | null>>) formatOptionLabel={(o) => formatOptionLabel(o as RegionOption)}
} onChange={(v) => onChange(v as SingleValue<RegionOption>)}
{...selectProps} {...selectProps}
/> />
); );
+1 -1
View File
@@ -45,7 +45,7 @@ export const classNames = <Option, IsMulti extends boolean = false>(
"border-gray-300 bg-gray-50 text-gray-900", "border-gray-300 bg-gray-50 text-gray-900",
"dark:border-gray-600 dark:bg-gray-700 dark:text-white", "dark:border-gray-600 dark:bg-gray-700 dark:text-white",
), ),
groupHeading: () => "ml-3 mt-2 mb-1 text-textGray text-sm uppercase", groupHeading: () => "mx-3 mt-2 mb-1 text-textGray text-sm uppercase",
option: ({ isFocused, isDisabled }) => option: ({ isFocused, isDisabled }) =>
twMerge( twMerge(
"px-3 py-2 hover:cursor-pointer", "px-3 py-2 hover:cursor-pointer",
+8 -5
View File
@@ -48,7 +48,7 @@
"FormValidation": { "FormValidation": {
"required": "Required", "required": "Required",
"url": "Invalid URL", "url": "Invalid URL",
"zone": "Please draw at least one region on the map" "zone": "Please draw at least one zone on the map"
}, },
"Home": { "Home": {
@@ -81,7 +81,6 @@
"searchLabel": "Search", "searchLabel": "Search",
"searchPlaceholder": "Search", "searchPlaceholder": "Search",
"syncWithMapOption": "Only tournaments visible in the map view", "syncWithMapOption": "Only tournaments visible in the map view",
"ignoreMapOption": "No region-based filter",
"normsOnly": "Only norm tournaments", "normsOnly": "Only norm tournaments",
"noneFound": "No tournaments found", "noneFound": "No tournaments found",
"date": "Date", "date": "Date",
@@ -98,7 +97,7 @@
"loading": "Loading...", "loading": "Loading...",
"searchLabel": "Search", "searchLabel": "Search",
"searchPlaceholder": "Search", "searchPlaceholder": "Search",
"syncWithMapCheckbox": "Clubs visible on the map", "syncWithMapOption": "Only clubs visible in the map view",
"noneFound": "No clubs found", "noneFound": "No clubs found",
"name": "Name", "name": "Name",
"ffe": "FFE", "ffe": "FFE",
@@ -227,8 +226,12 @@
"logonRequired": "You must be signed in to access this feature so that we can store your personalized Zones. There's no need to create an account, just sign in with your email address and we'll send you a link.", "logonRequired": "You must be signed in to access this feature so that we can store your personalized Zones. There's no need to create an account, just sign in with your email address and we'll send you a link.",
"noNotifications": "You have not enabled email notifications for this zone.", "noNotifications": "You have not enabled email notifications for this zone.",
"withNotifications": "You will receive email notifications for new tournaments with <b>{list}</b> time control in this region.", "withNotifications": "You will receive email notifications for new tournaments with <b>{list}</b> time control in this zone.",
"addZoneButton": "Add a Region", "addZoneButton": "Add a Zone",
"yourZonesSelectGroupHeader": "Your Zones",
"createZoneSelectOption": "Create a new zone",
"ignoreMapOption": "No region-based filter",
"createTitle": "Create a new zone", "createTitle": "Create a new zone",
"editTitle": "Edit zone", "editTitle": "Edit zone",
+5 -2
View File
@@ -80,7 +80,6 @@
"searchLabel": "Rechercher", "searchLabel": "Rechercher",
"searchPlaceholder": "Rechercher", "searchPlaceholder": "Rechercher",
"syncWithMapOption": "Seuls les tournois visibles dans la vue carte", "syncWithMapOption": "Seuls les tournois visibles dans la vue carte",
"ignoreMapOption": "Pas de filtre de région",
"normsOnly": "Uniquement tournois à normes", "normsOnly": "Uniquement tournois à normes",
"noneFound": "Pas de tournois trouvé", "noneFound": "Pas de tournois trouvé",
"date": "Date", "date": "Date",
@@ -97,7 +96,7 @@
"loading": "Téléchargement...", "loading": "Téléchargement...",
"searchLabel": "Rechercher", "searchLabel": "Rechercher",
"searchPlaceholder": "Rechercher", "searchPlaceholder": "Rechercher",
"syncWithMapCheckbox": "Clubs visibles sur la carte", "syncWithMapOption": "Seuls les clubs visibles dans la vue carte",
"noneFound": "Pas de club trouvé", "noneFound": "Pas de club trouvé",
"name": "Name", "name": "Name",
"ffe": "FFE", "ffe": "FFE",
@@ -231,6 +230,10 @@
"withNotifications": "Vous allez recevoir des notifications par e-mail pour les nouveaux tournois de cadences <b>{list}</b> dans cette région.", "withNotifications": "Vous allez recevoir des notifications par e-mail pour les nouveaux tournois de cadences <b>{list}</b> dans cette région.",
"addZoneButton": "Ajouter une région", "addZoneButton": "Ajouter une région",
"yourZonesSelectGroupHeader": "Your Zones",
"ignoreMapOption": "Pas de filtre de région",
"createZoneSelectOption": "Create a new zone",
"createTitle": "Créer une nouvelle région", "createTitle": "Créer une nouvelle région",
"editTitle": "Modifier région", "editTitle": "Modifier région",