Add member profile questions, admin tooling, legal pages, and fast tests
- Add configurable profile questions with conditional visibility, admin-only fields, user answers, and seeded onboarding/volunteer questions
- Add admin UI for managing profile questions and member profile answers
- Add volunteer level/profile data support across backend schemas, models, API, and migration
- Update dashboard/profile UI, super admin menu, membership service types, and related styling
- Add privacy policy, terms of service, cookie notice, and footer links
- Add frontend Vitest coverage for profile question logic
- Add backend pytest coverage for profile answer normalization and validation
- Update restart.sh to build, run frontend/backend unit tests, and restart only after tests pass
- Refresh README, quickstart, project structure, instructions, and Square docs to match current app features
- Protect feature flag reload behind super-admin access
- Restrict admin-triggered password resets so admins can only reset member accounts
- Replace email template HTML preview rendering with escaped text preview
- Update docs for feature flag reload access, password reset scope, and email template preview safety
-- test user questions are also made by AI and not very useful. but i didn't know what to put there so its good enough for a test
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
export type ProfileQuestionAnswerValue = string | number | boolean | null;
|
||||
|
||||
export interface EditableProfileQuestion {
|
||||
id: number;
|
||||
admin_only_edit: boolean;
|
||||
can_edit: boolean;
|
||||
}
|
||||
|
||||
export interface DependentProfileQuestion {
|
||||
id: number;
|
||||
depends_on_question_id: number | null;
|
||||
depends_on_value: string | null;
|
||||
}
|
||||
|
||||
export const answerToComparable = (value: ProfileQuestionAnswerValue): string | null => {
|
||||
if (value === null || value === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? 'true' : 'false';
|
||||
}
|
||||
|
||||
return String(value);
|
||||
};
|
||||
|
||||
export const canEditProfileQuestion = (
|
||||
question: EditableProfileQuestion,
|
||||
allowAdminManagedEdit = false
|
||||
): boolean => {
|
||||
if (!question.can_edit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (question.admin_only_edit && !allowAdminManagedEdit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
export const isProfileQuestionVisible = <TQuestion extends DependentProfileQuestion>(
|
||||
question: TQuestion,
|
||||
questionsById: Map<number, TQuestion>,
|
||||
answers: Record<number, ProfileQuestionAnswerValue>
|
||||
): boolean => {
|
||||
if (!question.depends_on_question_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const parentQuestion = questionsById.get(question.depends_on_question_id);
|
||||
if (!parentQuestion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const parentAnswer = answerToComparable(answers[parentQuestion.id] ?? null);
|
||||
if (question.depends_on_value === null || question.depends_on_value === undefined) {
|
||||
return parentAnswer !== null && parentAnswer !== '';
|
||||
}
|
||||
|
||||
return parentAnswer === question.depends_on_value;
|
||||
};
|
||||
Reference in New Issue
Block a user