From 99adb77fcb5ec14cf485ac787ed7fd110398696c Mon Sep 17 00:00:00 2001 From: MartinBraquet Date: Sun, 14 Sep 2025 22:18:21 +0200 Subject: [PATCH] Pretty print bookmarked searches --- web/components/filters/search.tsx | 11 +- web/components/searches/button.tsx | 172 ++++++++++++++++++++++------- web/lib/supabase/searches.ts | 7 +- 3 files changed, 144 insertions(+), 46 deletions(-) diff --git a/web/components/filters/search.tsx b/web/components/filters/search.tsx index 5b530dfa..f1208940 100644 --- a/web/components/filters/search.tsx +++ b/web/components/filters/search.tsx @@ -16,6 +16,7 @@ import {submitBookmarkedSearch} from "web/lib/supabase/searches"; import {useUser} from "web/hooks/use-user"; import {isEqual} from "lodash"; import {initialFilters} from "web/components/filters/use-filters"; +import toast from "react-hot-toast"; export type FilterFields = { orderBy: 'last_online_time' | 'created_time' | 'compatibility_score' @@ -195,8 +196,13 @@ export const Search = (props: { } loading={loadingBookmark} onClick={() => { + if (bookmarkedSearches.length >= 10) { + toast.error('You cannot bookmark more searches; please delete one first.') + setOpenBookmarks(true) + return + } setLoadingBookmark(true) - submitBookmarkedSearch(filters, user?.id) + submitBookmarkedSearch(filters, locationFilterProps, user?.id) .finally(() => { setLoadingBookmark(false) setBookmarked(true) @@ -214,7 +220,8 @@ export const Search = (props: { diff --git a/web/components/searches/button.tsx b/web/components/searches/button.tsx index 25feb70f..b6ee4454 100644 --- a/web/components/searches/button.tsx +++ b/web/components/searches/button.tsx @@ -1,32 +1,30 @@ import {User} from "common/user"; -import {useEffect, useState} from "react"; +import {ReactElement} from "react"; import {Button} from "web/components/buttons/button"; import {Modal, MODAL_CLASS} from "web/components/layout/modal"; import {Col} from "web/components/layout/col"; import {BookmarkedSearchesType} from "web/hooks/use-bookmarked-searches"; import {useUser} from "web/hooks/use-user"; import {initialFilters} from "web/components/filters/use-filters"; -import {Row} from "web/components/layout/row"; import {deleteBookmarkedSearch} from "web/lib/supabase/searches"; import {FilterFields} from "web/components/filters/search"; +import {hasKidsNames} from "web/components/filters/has-kids-filter"; +import {wantsKidsNames} from "web/components/filters/wants-kids-filter"; export function BookmarkSearchButton(props: { bookmarkedSearches: BookmarkedSearchesType[] refreshBookmarkedSearches: () => void - openBookmarks?: boolean + open: boolean + setOpen: (checked: boolean) => void }) { const { bookmarkedSearches, refreshBookmarkedSearches, - openBookmarks, + open, + setOpen, } = props - const [open, setOpen] = useState(false) const user = useUser() - useEffect(() => { - if (openBookmarks) setOpen(true) - }, [openBookmarks]); - if (!user) return null return ( <> @@ -44,6 +42,109 @@ export function BookmarkSearchButton(props: { ) } +// Define nice labels for each key +const filterLabels: Record = { + geodbCityIds: "", + location: "", + name: "Searching", + genders: "", + pref_age_max: "Max age", + pref_age_min: "Min age", + has_kids: "", + wants_kids_strength: "", + is_smoker: "", + pref_relation_styles: "Seeking", + pref_gender: "", + orderBy: "", +} + +export type locationType = { + location: { + name: string + } + radius: number +} + +export type FilterFieldsWithLocation = FilterFields & { + location: locationType +} + + +function formatFilters(filters: Partial): ReactElement | null { + const entries: ReactElement[] = [] + + let ageEntry = null + let ageMin: number | undefined = filters.pref_age_min + if (ageMin == 18) ageMin = undefined + let ageMax = filters.pref_age_max; + if (ageMax == 99) ageMax = undefined + if (ageMin || ageMax) { + let text: string = 'Age: ' + if (ageMin) text = `${text}${ageMin}` + if (ageMax) { + if (ageMin) { + text = `${text}-${ageMax}` + } else { + text = `${text}up to ${ageMax}` + } + } else { + text = `${text}+` + } + ageEntry = {text} + } + + Object.entries(filters).forEach(([key, value]) => { + const typedKey = key as keyof FilterFields + + if (!value) return + if (typedKey == 'pref_age_min' || typedKey == 'pref_age_max' || typedKey == 'geodbCityIds') return + if (Array.isArray(value) && value.length === 0) return + if (initialFilters[typedKey] === value) return + + const label = filterLabels[typedKey] ?? key + + let stringValue = value + if (key === 'has_kids') stringValue = hasKidsNames[value as number] + if (key === 'wants_kids_strength') stringValue = wantsKidsNames[value as number] + if (key === 'location') stringValue = `${(value as locationType)?.location?.name} (${(value as locationType)?.radius}mi)` + if (Array.isArray(value)) stringValue = value.join(', ') + + if (!label) { + const str = String(stringValue) + stringValue = str.charAt(0).toUpperCase() + str.slice(1) + } + + let display: ReactElement + display = key === 'name' + ? {stringValue as string} + : <>{stringValue} + + entries.push( + + {label} + {label ? ': ' : ''} + {display} + + ) + }) + + if (ageEntry) entries.push(ageEntry) + + if (entries.length === 0) return null + + // Join with " • " separators + return ( + + {entries.map((entry, i) => ( + + {entry} + {i < entries.length - 1 ? ' • ' : ''} + + ))} + + ) +} + function ButtonModal(props: { open: boolean setOpen: (open: boolean) => void @@ -61,44 +162,31 @@ function ButtonModal(props: { }} > -
Bookmarked Searches
-

We'll notify you daily when new people match your searches below.

+

Bookmarked Searches

+

We'll notify you daily when new people match your searches below.

- {(bookmarkedSearches || []).map((search) => ( - -

- {JSON.stringify( - Object.fromEntries( - Object.entries(search.search_filters as Record).filter(([key, value]) => { - // skip null/undefined - if (value == null) return false +

    + {(bookmarkedSearches || []).map((search) => ( +
  1. + {formatFilters(search.search_filters as Partial)} + +
  2. + ))} +
- // skip empty arrays - if (Array.isArray(value) && value.length === 0) return false - - // keep if different from initialFilters - return initialFilters[key as keyof FilterFields] !== value - }) - - ) - )} -

- -
- - ))} {/*, - userId: string | undefined | null + locationFilterProps: any, + userId: string | undefined | null, ) => { if (!filters) return if (!userId) return - const row = {search_filters: filters, creator_id: userId} + const fullFilter = {...filters, ...{location: locationFilterProps}} + + const row = {search_filters: fullFilter, creator_id: userId} const input = { ...filterKeys(row, (key, _) => !['id', 'created_time', 'last_notified_at'].includes(key)), } as BookmarkedSearchSubmitType