fix(admin): support typed question responses

Render database-backed text, audio, photo, and choice responses across admin activity and user detail views. Add support for the both_answered match state while retaining legacy swipe behavior.
This commit is contained in:
jjbrunton committed 2026-08-27 21:37:34 +01:00
1 parent 01bb8225ae
commit 6df9442cd0
6 files changed
+175 -42

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
import type { AnswerType, QuestionType } from '@sauci/shared';
export type AdminResponseData =
| { type: 'text_answer'; text: string }
| { type: 'audio'; media_path: string; duration_seconds: number }
| { type: 'photo'; media_path: string }
| { type: 'who_likely'; chosen_user_id: string }
| Record<string, unknown>
| null;
export const QUESTION_TYPE_LABELS: Record<QuestionType, string> = {
swipe: 'Swipe',
text_answer: 'Text answer',
audio: 'Audio',
photo: 'Photo',
who_likely: 'Who is more likely',
};
export const MATCH_TYPE_LABELS = {
yes_yes: 'Both Yes!',
yes_maybe: 'Yes + Maybe',
maybe_maybe: 'Both Maybe',
both_answered: 'Both Answered',
} as const;
export function resolveQuestionType(questionType?: QuestionType | null): QuestionType {
return questionType ?? 'swipe';
}
export function formatAdminResponse(
questionType: QuestionType | null | undefined,
answer: AnswerType,
responseData: AdminResponseData,
): { label: string; detail: string | null } {
const resolvedType = resolveQuestionType(questionType);
if (resolvedType === 'swipe') {
return {
label: answer.charAt(0).toUpperCase() + answer.slice(1),
detail: null,
};
}
if (resolvedType === 'text_answer') {
const text = responseData && 'text' in responseData && typeof responseData.text === 'string'
? responseData.text.trim()
: '';
return { label: 'Text submitted', detail: text || null };
}
if (resolvedType === 'audio') {
const duration = responseData && 'duration_seconds' in responseData
&& typeof responseData.duration_seconds === 'number'
? Math.max(0, Math.round(responseData.duration_seconds))
: null;
return {
label: 'Audio submitted',
detail: duration === null ? null : `${duration}s recording`,
};
}
if (resolvedType === 'photo') {
return { label: 'Photo submitted', detail: null };
}
return { label: 'Choice submitted', detail: null };
}