mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-01-30 00:21:43 -05:00
Fix interest (etc.) shown as IDs instead of labels in saved searches and emails
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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 <compass@compassmeet.com>'
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
),
|
||||
})
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -13,6 +13,7 @@ interface NewMessageEmailProps {
|
||||
matches: MatchesType[]
|
||||
unsubscribeUrl: string
|
||||
email?: string
|
||||
optionIdsToLabels?: Record<string, Record<string, string>>
|
||||
}
|
||||
|
||||
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 = ({
|
||||
<Text style={{fontWeight: "bold", marginBottom: "5px"}}>
|
||||
{formatFilters(
|
||||
match.description.filters as Partial<FilterFields>,
|
||||
match.description.location as locationType
|
||||
match.description.location as locationType,
|
||||
optionIdsToLabels,
|
||||
)?.join(" • ")}
|
||||
</Text>
|
||||
<Text style={{margin: 0}}>
|
||||
|
||||
51
backend/shared/src/supabase/options.ts
Normal file
51
backend/shared/src/supabase/options.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import {createSupabaseDirectClient} from 'shared/supabase/init'
|
||||
import {OPTION_TABLES, OptionTableKey} from 'common/profiles/constants'
|
||||
|
||||
interface CacheEntry {
|
||||
data: Record<OptionTableKey, Record<string, string>>
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
const cache = new Map<string, CacheEntry>()
|
||||
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<OptionTableKey, Record<string, string>> = {} as Record<OptionTableKey, Record<string, string>>
|
||||
|
||||
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<string, string> = {}
|
||||
rows.forEach(row => idToName[row.id] = row.name)
|
||||
result[tableKey] = idToName
|
||||
}
|
||||
|
||||
cache.set(cacheKey, {
|
||||
data: result,
|
||||
timestamp: Date.now()
|
||||
})
|
||||
// console.log({result})
|
||||
return result
|
||||
}
|
||||
@@ -18,6 +18,9 @@ const filterLabels: Record<string, string> = {
|
||||
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<FilterFields>, location: locationType | null): String[] | null {
|
||||
export function formatFilters(
|
||||
filters: Partial<FilterFields>,
|
||||
location: locationType | null,
|
||||
choicesIdsToLabels: Record<string, any>
|
||||
): String[] | null {
|
||||
const entries: String[] = []
|
||||
|
||||
let ageEntry = null
|
||||
@@ -83,7 +90,12 @@ export function formatFilters(filters: Partial<FilterFields>, 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)
|
||||
|
||||
@@ -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 (
|
||||
<Modal
|
||||
open={open}
|
||||
@@ -91,7 +93,7 @@ function ButtonModal(props: {
|
||||
{(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>, search.location as locationType)?.join(" • ")}
|
||||
{formatFilters(search.search_filters as Partial<FilterFields>, search.location as locationType, choicesIdsToLabels)?.join(" • ")}
|
||||
<button
|
||||
onClick={async () => {
|
||||
await deleteBookmarkedSearch(search.id)
|
||||
|
||||
@@ -3,8 +3,9 @@ import {run} from 'common/supabase/utils'
|
||||
import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-state'
|
||||
import {db} from 'web/lib/supabase/db'
|
||||
import {useLocale} from "web/lib/locale";
|
||||
import {OptionTableKey} from "common/profiles/constants";
|
||||
|
||||
export async function fetchChoices(label: string, locale: string) {
|
||||
export async function fetchChoices(label: OptionTableKey, locale: string) {
|
||||
let choicesById: Record<string, string> = {};
|
||||
const {data} = await run(
|
||||
db
|
||||
@@ -29,7 +30,7 @@ export async function fetchChoices(label: string, locale: string) {
|
||||
return choicesById
|
||||
}
|
||||
|
||||
export const useChoices = (label: string) => {
|
||||
export const useChoices = (label: OptionTableKey) => {
|
||||
const [choices, setChoices] = usePersistentInMemoryState<Record<string, string>>({}, `${label}-choices`)
|
||||
const {locale} = useLocale()
|
||||
|
||||
@@ -50,3 +51,10 @@ export const useChoices = (label: string) => {
|
||||
|
||||
return {choices, refreshChoices}
|
||||
}
|
||||
|
||||
export const useAllChoices = () => {
|
||||
const {choices: interests, refreshChoices: refreshInterests} = useChoices('interests')
|
||||
const {choices: causes, refreshChoices: refreshCauses} = useChoices('causes')
|
||||
const {choices: work, refreshChoices: refreshWork} = useChoices('work')
|
||||
return {interests, causes, work, refreshInterests, refreshCauses, refreshWork}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user