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:
2026-05-04 22:05:58 +01:00
parent 74a4e3ede8
commit 632e66e21d
34 changed files with 3932 additions and 749 deletions
@@ -0,0 +1,42 @@
import { describe, expect, it } from 'vitest';
import {
canEditProfileQuestion,
DependentProfileQuestion,
isProfileQuestionVisible,
ProfileQuestionAnswerValue
} from './profileQuestionLogic';
describe('profile question logic', () => {
it('keeps admin-managed questions read-only outside admin editing mode', () => {
const question = {
id: 1,
admin_only_edit: true,
can_edit: true
};
expect(canEditProfileQuestion(question, false)).toBe(false);
expect(canEditProfileQuestion(question, true)).toBe(true);
});
it('does not allow editing when the API marks a question read-only', () => {
expect(canEditProfileQuestion({ id: 1, admin_only_edit: false, can_edit: false }, true)).toBe(false);
});
it('shows dependent questions when boolean answers match', () => {
const parent: DependentProfileQuestion = { id: 1, depends_on_question_id: null, depends_on_value: null };
const child: DependentProfileQuestion = { id: 2, depends_on_question_id: 1, depends_on_value: 'true' };
const questionsById = new Map<number, DependentProfileQuestion>([[parent.id, parent], [child.id, child]]);
const answers: Record<number, ProfileQuestionAnswerValue> = { 1: true };
expect(isProfileQuestionVisible(child, questionsById, answers)).toBe(true);
});
it('hides dependent questions when select answers do not match', () => {
const parent: DependentProfileQuestion = { id: 1, depends_on_question_id: null, depends_on_value: null };
const child: DependentProfileQuestion = { id: 2, depends_on_question_id: 1, depends_on_value: 'completed' };
const questionsById = new Map<number, DependentProfileQuestion>([[parent.id, parent], [child.id, child]]);
const answers: Record<number, ProfileQuestionAnswerValue> = { 1: 'pending' };
expect(isProfileQuestionVisible(child, questionsById, answers)).toBe(false);
});
});