mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-05-13 17:46:52 -04:00
Add translation support for compatibility prompts
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@compass/api",
|
||||
"description": "Backend API endpoints",
|
||||
"version": "1.0.15",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"watch:serve": "tsx watch src/serve.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type APIHandler } from 'api/helpers/endpoint'
|
||||
import { createSupabaseDirectClient } from 'shared/supabase/init'
|
||||
import { Row } from 'common/supabase/utils'
|
||||
import {type APIHandler} from 'api/helpers/endpoint'
|
||||
import {createSupabaseDirectClient} from 'shared/supabase/init'
|
||||
import {Row} from 'common/supabase/utils'
|
||||
|
||||
export function shuffle<T>(array: T[]): T[] {
|
||||
const arr = [...array]; // copy to avoid mutating the original
|
||||
@@ -13,30 +13,57 @@ export function shuffle<T>(array: T[]): T[] {
|
||||
|
||||
export const getCompatibilityQuestions: APIHandler<
|
||||
'get-compatibility-questions'
|
||||
> = async (_props, _auth) => {
|
||||
> = async (props, _auth) => {
|
||||
const {locale = 'en'} = props
|
||||
const pg = createSupabaseDirectClient()
|
||||
|
||||
const questions = await pg.manyOrNone<
|
||||
Row<'compatibility_prompts'> & { answer_count: number; score: number }
|
||||
>(
|
||||
`SELECT
|
||||
compatibility_prompts.*,
|
||||
COUNT(compatibility_answers.question_id) as answer_count,
|
||||
AVG(POWER(compatibility_answers.importance + 1 + CASE WHEN compatibility_answers.explanation IS NULL THEN 1 ELSE 0 END, 2)) as score
|
||||
FROM
|
||||
compatibility_prompts
|
||||
LEFT JOIN
|
||||
compatibility_answers ON compatibility_prompts.id = compatibility_answers.question_id
|
||||
WHERE
|
||||
compatibility_prompts.answer_type = 'compatibility_multiple_choice'
|
||||
GROUP BY
|
||||
compatibility_prompts.id
|
||||
ORDER BY
|
||||
compatibility_prompts.importance_score
|
||||
`
|
||||
SELECT cp.id,
|
||||
cp.answer_type,
|
||||
cp.importance_score,
|
||||
cp.created_time,
|
||||
cp.creator_id,
|
||||
cp.category,
|
||||
|
||||
-- locale-aware fields
|
||||
COALESCE(cpt.question, cp.question) AS question,
|
||||
COALESCE(cpt.multiple_choice_options, cp.multiple_choice_options) AS multiple_choice_options,
|
||||
|
||||
COUNT(ca.question_id) AS answer_count,
|
||||
AVG(
|
||||
POWER(
|
||||
ca.importance + 1 +
|
||||
CASE WHEN ca.explanation IS NULL THEN 1 ELSE 0 END,
|
||||
2
|
||||
)
|
||||
) AS score
|
||||
|
||||
FROM compatibility_prompts cp
|
||||
|
||||
LEFT JOIN compatibility_answers ca
|
||||
ON cp.id = ca.question_id
|
||||
|
||||
LEFT JOIN compatibility_prompts_translations cpt
|
||||
ON cp.id = cpt.question_id
|
||||
AND cpt.locale = $1
|
||||
AND $1 <> 'en'
|
||||
|
||||
WHERE cp.answer_type = 'compatibility_multiple_choice'
|
||||
|
||||
GROUP BY cp.id,
|
||||
cpt.question,
|
||||
cpt.multiple_choice_options
|
||||
|
||||
ORDER BY cp.importance_score
|
||||
`,
|
||||
[]
|
||||
[locale]
|
||||
)
|
||||
|
||||
// console.debug({questions})
|
||||
|
||||
// const questions = shuffle(dbQuestions)
|
||||
|
||||
// console.debug(
|
||||
|
||||
44
backend/supabase/compatibility_prompts_translations.sql
Normal file
44
backend/supabase/compatibility_prompts_translations.sql
Normal file
@@ -0,0 +1,44 @@
|
||||
CREATE TABLE IF NOT EXISTS compatibility_prompts_translations
|
||||
(
|
||||
question_id BIGINT NOT NULL,
|
||||
locale TEXT NOT NULL,
|
||||
multiple_choice_options JSONB,
|
||||
question TEXT NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||||
PRIMARY KEY (question_id, locale),
|
||||
CONSTRAINT fk_question
|
||||
FOREIGN KEY (question_id)
|
||||
REFERENCES compatibility_prompts (id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Add triggers for updated_at
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS
|
||||
$$
|
||||
BEGIN
|
||||
NEW.updated_at = now();
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
CREATE TRIGGER update_compatibility_prompts_translations_updated_at
|
||||
BEFORE UPDATE
|
||||
ON compatibility_prompts_translations
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
-- Add RLS policies
|
||||
ALTER TABLE compatibility_prompts_translations
|
||||
ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- Public read access
|
||||
CREATE POLICY "public read"
|
||||
ON compatibility_prompts_translations
|
||||
FOR SELECT
|
||||
USING (true);
|
||||
|
||||
-- Indexes for better performance
|
||||
CREATE INDEX IF NOT EXISTS idx_compatibility_prompts_translations_locale ON compatibility_prompts_translations (locale);
|
||||
CREATE INDEX IF NOT EXISTS idx_compatibility_prompts_translations_question_id ON compatibility_prompts_translations (question_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_cpt_locale ON compatibility_prompts_translations (question_id, locale);
|
||||
@@ -345,7 +345,9 @@ export const API = (_apiTypeCheck = {
|
||||
method: 'GET',
|
||||
authed: true,
|
||||
rateLimited: false,
|
||||
props: z.object({}),
|
||||
props: z.object({
|
||||
locale: z.string().optional()
|
||||
}),
|
||||
returns: {} as {
|
||||
status: 'success'
|
||||
questions: (Row<'compatibility_prompts'> & {
|
||||
|
||||
@@ -198,6 +198,38 @@ export type Database = {
|
||||
},
|
||||
]
|
||||
}
|
||||
compatibility_prompts_translations: {
|
||||
Row: {
|
||||
locale: string
|
||||
multiple_choice_options: Json | null
|
||||
question: string
|
||||
question_id: number
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
locale: string
|
||||
multiple_choice_options?: Json | null
|
||||
question: string
|
||||
question_id: number
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
locale?: string
|
||||
multiple_choice_options?: Json | null
|
||||
question?: string
|
||||
question_id?: number
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: 'fk_question'
|
||||
columns: ['question_id']
|
||||
isOneToOne: false
|
||||
referencedRelation: 'compatibility_prompts'
|
||||
referencedColumns: ['id']
|
||||
},
|
||||
]
|
||||
}
|
||||
compatibility_scores: {
|
||||
Row: {
|
||||
created_time: string
|
||||
@@ -1301,8 +1333,8 @@ export type Database = {
|
||||
}[]
|
||||
}
|
||||
is_admin:
|
||||
| { Args: { user_id: string }; Returns: boolean }
|
||||
| { Args: never; Returns: boolean }
|
||||
| { Args: { user_id: string }; Returns: boolean }
|
||||
millis_interval: {
|
||||
Args: { end_millis: number; start_millis: number }
|
||||
Returns: unknown
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { sortBy } from 'lodash'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Row } from 'common/supabase/utils'
|
||||
import {sortBy} from 'lodash'
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Row} from 'common/supabase/utils'
|
||||
import {
|
||||
getAllQuestions,
|
||||
getFRQuestionsWithAnswerCount,
|
||||
getFreeResponseQuestions,
|
||||
getFRQuestionsWithAnswerCount,
|
||||
getUserAnswers,
|
||||
getUserCompatibilityAnswers,
|
||||
} from 'web/lib/supabase/questions'
|
||||
import { usePersistentInMemoryState } from 'web/hooks/use-persistent-in-memory-state'
|
||||
import { api } from 'web/lib/api'
|
||||
import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-state'
|
||||
import {api} from 'web/lib/api'
|
||||
import {useLocale} from "web/lib/locale";
|
||||
|
||||
export const useQuestions = () => {
|
||||
const [questions, setQuestions] = useState<Row<'compatibility_prompts'>[]>([])
|
||||
@@ -93,6 +94,7 @@ export const useFRQuestionsWithAnswerCount = () => {
|
||||
}
|
||||
|
||||
export const useCompatibilityQuestionsWithAnswerCount = () => {
|
||||
const {locale} = useLocale()
|
||||
const [compatibilityQuestions, setCompatibilityQuestions] =
|
||||
usePersistentInMemoryState<QuestionWithCountType[]>(
|
||||
[],
|
||||
@@ -100,14 +102,14 @@ export const useCompatibilityQuestionsWithAnswerCount = () => {
|
||||
)
|
||||
|
||||
async function refreshCompatibilityQuestions() {
|
||||
return api('get-compatibility-questions', {}).then((res) => {
|
||||
return api('get-compatibility-questions', {locale}).then((res) => {
|
||||
setCompatibilityQuestions(res.questions)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
refreshCompatibilityQuestions()
|
||||
}, [])
|
||||
}, [locale])
|
||||
|
||||
return {
|
||||
refreshCompatibilityQuestions,
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"answers.add.submit_own": "Proposez la vôtre !",
|
||||
"answers.answer.answer_skipped": "Répondre à {n} questions ignorées",
|
||||
"answers.answer.answer_yourself": "Répondez vous-même",
|
||||
"answers.answer.cta": "Répondre{core} aux questions",
|
||||
"answers.answer.cta": "Répondre aux questions",
|
||||
"answers.answer.view_list": "Voir la liste des questions",
|
||||
"answers.compatible": "Compatible",
|
||||
"answers.content.answers_you_accept": "Réponses que vous acceptez",
|
||||
|
||||
Reference in New Issue
Block a user