mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-04-13 11:09:26 -04:00
Pretty print bookmarked searches
This commit is contained in:
@@ -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: {
|
||||
<BookmarkSearchButton
|
||||
refreshBookmarkedSearches={refreshBookmarkedSearches}
|
||||
bookmarkedSearches={bookmarkedSearches}
|
||||
openBookmarks={openBookmarks}
|
||||
open={openBookmarks}
|
||||
setOpen={setOpenBookmarks}
|
||||
/>
|
||||
</Row>
|
||||
</Col>
|
||||
|
||||
@@ -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<string, string> = {
|
||||
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<FilterFieldsWithLocation>): 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 = <span>{text}</span>
|
||||
}
|
||||
|
||||
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'
|
||||
? <i>{stringValue as string}</i>
|
||||
: <>{stringValue}</>
|
||||
|
||||
entries.push(
|
||||
<span key={key}>
|
||||
{label}
|
||||
{label ? ': ' : ''}
|
||||
{display}
|
||||
</span>
|
||||
)
|
||||
})
|
||||
|
||||
if (ageEntry) entries.push(ageEntry)
|
||||
|
||||
if (entries.length === 0) return null
|
||||
|
||||
// Join with " • " separators
|
||||
return (
|
||||
<span>
|
||||
{entries.map((entry, i) => (
|
||||
<span key={i}>
|
||||
{entry}
|
||||
{i < entries.length - 1 ? ' • ' : ''}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function ButtonModal(props: {
|
||||
open: boolean
|
||||
setOpen: (open: boolean) => void
|
||||
@@ -61,44 +162,31 @@ function ButtonModal(props: {
|
||||
}}
|
||||
>
|
||||
<Col className={MODAL_CLASS}>
|
||||
<div>Bookmarked Searches</div>
|
||||
<p className='text-xs'>We'll notify you daily when new people match your searches below.</p>
|
||||
<h3>Bookmarked Searches</h3>
|
||||
<p className='text-sm'>We'll notify you daily when new people match your searches below.</p>
|
||||
<Col
|
||||
className={
|
||||
'border-ink-300 text-ink-400 bg-canvas-0 inline-flex flex-col gap-2 rounded-md border p-1 text-sm shadow-sm'
|
||||
'border-ink-300bg-canvas-0 inline-flex flex-col gap-2 rounded-md border p-1 text-sm shadow-sm'
|
||||
}
|
||||
>
|
||||
{(bookmarkedSearches || []).map((search) => (
|
||||
<Row key={search.id}>
|
||||
<p>
|
||||
{JSON.stringify(
|
||||
Object.fromEntries(
|
||||
Object.entries(search.search_filters as Record<string, any>).filter(([key, value]) => {
|
||||
// skip null/undefined
|
||||
if (value == null) return false
|
||||
<ol className="list-decimal list-inside space-y-2">
|
||||
{(bookmarkedSearches || []).map((search) => (
|
||||
<li key={search.id}
|
||||
className="items-center justify-between gap-2 list-item marker:text-ink-500 marker:font-bold">
|
||||
{formatFilters(search.search_filters as Partial<FilterFields>)}
|
||||
<button
|
||||
onClick={async () => {
|
||||
await deleteBookmarkedSearch(search.id)
|
||||
refreshBookmarkedSearches()
|
||||
}}
|
||||
className="inline-flex text-xl h-5 w-5 items-center justify-center rounded-full text-red-600 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-red-400"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
|
||||
// skip empty arrays
|
||||
if (Array.isArray(value) && value.length === 0) return false
|
||||
|
||||
// keep if different from initialFilters
|
||||
return initialFilters[key as keyof FilterFields] !== value
|
||||
})
|
||||
|
||||
)
|
||||
)}
|
||||
</p>
|
||||
<button
|
||||
onClick={async () => {
|
||||
await deleteBookmarkedSearch(search.id)
|
||||
refreshBookmarkedSearches()
|
||||
}}
|
||||
className="inline-flex h-4 w-4 items-center justify-center rounded-full text-red-600 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-red-400"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</Row>
|
||||
|
||||
))}
|
||||
</Col>
|
||||
{/*<BookmarkSearchContent*/}
|
||||
{/* total={bookmarkedSearches.length}*/}
|
||||
|
||||
@@ -23,11 +23,14 @@ export type BookmarkedSearchSubmitType = Omit<
|
||||
|
||||
export const submitBookmarkedSearch = async (
|
||||
filters: Partial<FilterFields>,
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user