diff --git a/backend/api/package.json b/backend/api/package.json index 2e9a9fc..5e71706 100644 --- a/backend/api/package.json +++ b/backend/api/package.json @@ -1,7 +1,7 @@ { "name": "@compass/api", "description": "Backend API endpoints", - "version": "1.2.1", + "version": "1.2.2", "private": true, "scripts": { "watch:serve": "tsx watch src/serve.ts", diff --git a/backend/email/emails/functions/helpers.tsx b/backend/email/emails/functions/helpers.tsx index dfc06b6..4fa16e5 100644 --- a/backend/email/emails/functions/helpers.tsx +++ b/backend/email/emails/functions/helpers.tsx @@ -11,6 +11,7 @@ import {MatchesType} from "common/profiles/bookmarked_searches"; import NewSearchAlertsEmail from "email/new-search_alerts"; import WelcomeEmail from "email/welcome"; import * as admin from "firebase-admin"; +import {getOptionsIdsToLabels} from "shared/supabase/options"; export const fromEmail = 'Compass ' @@ -111,6 +112,11 @@ export const sendSearchAlertsEmail = async ( const email = privateUser.email; if (!email || !sendToEmail) return + // Determine locale (fallback to 'en') and load option labels before rendering + // TODO: add locale to user array + const locale = (toUser as any)?.locale ?? 'en' + const optionIdsToLabels = await getOptionsIdsToLabels(locale) + return await sendEmail({ from: fromEmail, subject: `People aligned with your values just joined`, @@ -121,6 +127,7 @@ export const sendSearchAlertsEmail = async ( matches={matches} unsubscribeUrl={unsubscribeUrl} email={email} + optionIdsToLabels={optionIdsToLabels} /> ), }) diff --git a/backend/email/emails/functions/send-email.ts b/backend/email/emails/functions/send-email.ts index 2dde84c..ed32de6 100644 --- a/backend/email/emails/functions/send-email.ts +++ b/backend/email/emails/functions/send-email.ts @@ -1,9 +1,5 @@ -import { - CreateEmailRequestOptions, - Resend, - type CreateEmailOptions, -} from 'resend' -import { log } from 'shared/utils' +import {type CreateEmailOptions, CreateEmailRequestOptions, Resend,} from 'resend' +import {log} from 'shared/utils' import {sleep} from "common/util/time"; @@ -17,6 +13,12 @@ export const sendEmail = async ( const resend = getResend() console.debug(resend, payload, options) + const skip = false + if (skip) { + console.warn("Skipping email send") + return null + } + if (!resend) return null const { data, error } = await resend.emails.send( diff --git a/backend/email/emails/new-search_alerts.tsx b/backend/email/emails/new-search_alerts.tsx index 185e9a4..6a216ef 100644 --- a/backend/email/emails/new-search_alerts.tsx +++ b/backend/email/emails/new-search_alerts.tsx @@ -13,6 +13,7 @@ interface NewMessageEmailProps { matches: MatchesType[] unsubscribeUrl: string email?: string + optionIdsToLabels?: Record> } export const NewSearchAlertsEmail = ({ @@ -20,6 +21,7 @@ export const NewSearchAlertsEmail = ({ unsubscribeUrl, matches, email, + optionIdsToLabels = {}, }: NewMessageEmailProps) => { const name = toUser.name.split(' ')[0] @@ -44,7 +46,8 @@ export const NewSearchAlertsEmail = ({ {formatFilters( match.description.filters as Partial, - match.description.location as locationType + match.description.location as locationType, + optionIdsToLabels, )?.join(" • ")} diff --git a/backend/shared/src/supabase/options.ts b/backend/shared/src/supabase/options.ts new file mode 100644 index 0000000..378890e --- /dev/null +++ b/backend/shared/src/supabase/options.ts @@ -0,0 +1,51 @@ +import {createSupabaseDirectClient} from 'shared/supabase/init' +import {OPTION_TABLES, OptionTableKey} from 'common/profiles/constants' + +interface CacheEntry { + data: Record> + timestamp: number +} + +const cache = new Map() +const CACHE_TTL = 60 * 60 * 1000 // 1 hour in milliseconds + +export async function getOptionsIdsToLabels(locale: string = 'en') { + const cacheKey = `options-${locale}` + const now = Date.now() + + const cached = cache.get(cacheKey) + if (cached && (now - cached.timestamp) < CACHE_TTL) { + return cached.data + } + // console.log("Fetching getOptionsIdsToLabels...") + const pg = createSupabaseDirectClient() + const result: Record> = {} as Record> + + for (const tableKey of OPTION_TABLES) { + // const rows = await pg.manyOrNone(` + // SELECT + // id, + // COALESCE( + // (${tableKey}_translations.name) FILTER (WHERE ${tableKey}_translations.locale = $1), + // name + // ) as name + // FROM ${tableKey} + // LEFT JOIN ${tableKey}_translations ON ${tableKey}.id = ${tableKey}_translations.id + // ORDER BY name ASC + // `, [locale]) + const rows = await pg.manyOrNone(`SELECT id, name + FROM ${tableKey} + ORDER BY name`, [locale]) + + const idToName: Record = {} + rows.forEach(row => idToName[row.id] = row.name) + result[tableKey] = idToName + } + + cache.set(cacheKey, { + data: result, + timestamp: Date.now() + }) + // console.log({result}) + return result +} \ No newline at end of file diff --git a/common/src/searches.ts b/common/src/searches.ts index 4b5a5ab..0d054a8 100644 --- a/common/src/searches.ts +++ b/common/src/searches.ts @@ -18,6 +18,9 @@ const filterLabels: Record = { wants_kids_strength: "Kids", is_smoker: "", pref_relation_styles: "Seeking", + interests: "", + causes: "", + work: "", religion: "", pref_gender: "", orderBy: "", @@ -47,7 +50,11 @@ const skippedKeys = [ ] -export function formatFilters(filters: Partial, location: locationType | null): String[] | null { +export function formatFilters( + filters: Partial, + location: locationType | null, + choicesIdsToLabels: Record +): String[] | null { const entries: String[] = [] let ageEntry = null @@ -83,7 +90,12 @@ export function formatFilters(filters: Partial, location: location let stringValue = value if (key === 'has_kids') stringValue = hasKidsNames[value as number] if (key === 'wants_kids_strength') stringValue = wantsKidsNames[value as number] - if (Array.isArray(value)) stringValue = value.join(', ') + if (Array.isArray(value)) { + if (choicesIdsToLabels[key]) { + value = value.map((id) => choicesIdsToLabels[key][id]) + } + stringValue = value.join(', ') + } if (!label) { const str = String(stringValue) diff --git a/web/components/searches/button.tsx b/web/components/searches/button.tsx index c3afc2f..dd50164 100644 --- a/web/components/searches/button.tsx +++ b/web/components/searches/button.tsx @@ -13,6 +13,7 @@ import {useState} from "react"; import {useT} from "web/lib/locale"; import toast from "react-hot-toast"; import Link from "next/link"; +import {useAllChoices} from "web/hooks/use-choices"; export function BookmarkSearchButton(props: { bookmarkedSearches: BookmarkedSearchesType[] @@ -70,6 +71,7 @@ function ButtonModal(props: { }) { const {open, setOpen, bookmarkedSearches, refreshBookmarkedSearches} = props const t = useT() + const choicesIdsToLabels = useAllChoices() return ( (
  • - {formatFilters(search.search_filters as Partial, search.location as locationType)?.join(" • ")} + {formatFilters(search.search_filters as Partial, search.location as locationType, choicesIdsToLabels)?.join(" • ")}