import {BookmarkIcon, XMarkIcon} from '@heroicons/react/24/outline' import clsx from 'clsx' import {DisplayUser} from 'common/api/user-types' import {FilterFields} from 'common/filters' import {formatFilters, locationType} from 'common/filters-format' import {User} from 'common/user' import {Users} from 'lucide-react' import Link from 'next/link' import {useState} from 'react' import toast from 'react-hot-toast' import {Button} from 'web/components/buttons/button' import {Col} from 'web/components/layout/col' import {Modal, MODAL_CLASS, SCROLLABLE_MODAL_CLASS} from 'web/components/layout/modal' import {Row} from 'web/components/layout/row' import {Avatar} from 'web/components/widgets/avatar' import {BookmarkedSearchesType} from 'web/hooks/use-bookmarked-searches' import {useChoicesContext} from 'web/hooks/use-choices' import {useMeasurementSystem} from 'web/hooks/use-measurement-system' import {useUser} from 'web/hooks/use-user' import {api} from 'web/lib/api' import {useT} from 'web/lib/locale' import {deleteBookmarkedSearch} from 'web/lib/supabase/searches' export function BookmarkSearchButton(props: { bookmarkedSearches: BookmarkedSearchesType[] refreshBookmarkedSearches: () => void open: boolean setOpen: (checked: boolean) => void }) { const {bookmarkedSearches, refreshBookmarkedSearches, open, setOpen} = props const user = useUser() const t = useT() if (!user) return null return ( <> setOpen(true)} // color="gray-outline" // size={'xs'} className={ 'rounded-xl bg-canvas-50 text-xs border-canvas-300 flex items-center gap-1.5 border px-3 py-2 text-ink-500 transition-colors hover:border-primary-400 hover:bg-primary-50' } > {' '} {t('saved_searches.button', 'Saved Searches')} > ) } export function ResetFiltersButton(props: {clearFilters: () => void}) { const {clearFilters} = props const t = useT() return ( <> {t('filter.reset', 'Reset filters')} > ) } function ButtonModal(props: { open: boolean setOpen: (open: boolean) => void user: User bookmarkedSearches: BookmarkedSearchesType[] refreshBookmarkedSearches: () => void }) { const {open, setOpen, bookmarkedSearches, refreshBookmarkedSearches} = props const t = useT() const choicesIdsToLabels = useChoicesContext() const {measurementSystem} = useMeasurementSystem() return ( { refreshBookmarkedSearches() }} size={'lg'} > {t('saved_searches.title', 'Saved Searches')} {bookmarkedSearches?.length ? ( <> {t( 'saved_searches.notification_note', "We'll notify you daily when new people match your searches below.", )} {(bookmarkedSearches || []).map((search) => ( {formatFilters( search.search_filters as Partial, search.location as locationType, choicesIdsToLabels, measurementSystem, t, )?.join(' • ')} { await deleteBookmarkedSearch(search.id) refreshBookmarkedSearches() }} className="inline-flex items-center justify-center h-8 w-8 rounded-lg border border-canvas-300 bg-canvas-0 text-ink-500 hover:border-red-300 hover:bg-red-50 hover:text-red-600 transition-all focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-offset-1" > ))} > ) : ( You haven't saved any search. {t( 'saved_searches.empty_state', "To save one, click on Get Notified and we'll notify you daily when new people match it.", )} )} {/* {*/} {/* setOpen(false)*/} {/* }}*/} {/* isLastQuestion={questionIndex === bookmarkedSearches.length - 1}*/} {/* onNext={() => {*/} {/* if (questionIndex === bookmarkedSearches.length - 1) {*/} {/* setOpen(false)*/} {/* } else {*/} {/* setQuestionIndex(questionIndex + 1)*/} {/* }*/} {/* }}*/} {/*/>*/} ) } export function BookmarkStarButton(props: { starredUsers: DisplayUser[] refreshStars: () => void open: boolean setOpen: (checked: boolean) => void }) { const {starredUsers, refreshStars, open, setOpen} = props const user = useUser() const t = useT() if (!user) return null return ( <> setOpen(true)} color="gray-outline" // size={'xs'} className={ 'rounded-xl bg-canvas-50 text-xs border-canvas-300 flex items-center gap-1.5 border px-3 py-2 text-ink-500 transition-colors hover:border-primary-400 hover:bg-primary-50' } > {t('saved_people.button', 'Saved People')} > ) } function StarModal(props: { open: boolean setOpen: (open: boolean) => void user: User starredUsers: DisplayUser[] refreshStars: () => void }) { const {open, setOpen, starredUsers, refreshStars} = props // Track items being optimistically removed so we can hide them immediately const [removingIds, setRemovingIds] = useState>(new Set()) const t = useT() const visibleUsers = (starredUsers || []).filter((u) => !removingIds.has(u.id)) return ( { // refreshBookmarkedSearches() // }} > {t('saved_people.title', 'Saved People')} {visibleUsers?.length ? ( <> {t('saved_people.list_header', 'Here are the people you saved:')} {visibleUsers.map((u) => ( {u.name} @{u.username} { // Optimistically remove the user from the list setRemovingIds((prev) => new Set(prev).add(u.id)) // Fire the API call without blocking UI api('star-profile', { targetUserId: u.id, remove: true, }) .then(() => { // Sync with server state refreshStars() }) .catch(() => { toast.error("Couldn't remove saved profile. Please try again.") // Revert optimistic removal on failure setRemovingIds((prev) => { const next = new Set(prev) next.delete(u.id) return next }) }) }} className="inline-flex items-center justify-center h-8 w-8 rounded-lg border border-canvas-300 bg-canvas-0 text-ink-500 hover:border-red-300 hover:bg-red-50 hover:text-red-600 transition-all focus:outline-none focus:ring-2 focus:ring-red-400 focus:ring-offset-1" > ))} > ) : ( You haven't saved any profile. To save one, click on the bookmark on their profile page. )} {/* {*/} {/* setOpen(false)*/} {/* }}*/} {/* isLastQuestion={questionIndex === bookmarkedSearches.length - 1}*/} {/* onNext={() => {*/} {/* if (questionIndex === bookmarkedSearches.length - 1) {*/} {/* setOpen(false)*/} {/* } else {*/} {/* setQuestionIndex(questionIndex + 1)*/} {/* }*/} {/* }}*/} {/*/>*/} ) }
{t( 'saved_searches.notification_note', "We'll notify you daily when new people match your searches below.", )}
{t('saved_people.list_header', 'Here are the people you saved:')}