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
+99 -1
View File
@@ -1,5 +1,7 @@
from sqlalchemy.orm import Session
from ..models.models import MembershipTier, User, UserRole, EmailTemplate
import json
from ..models.models import MembershipTier, User, UserRole, EmailTemplate, ProfileQuestion
from .security import get_password_hash
from datetime import datetime
@@ -70,3 +72,99 @@ def init_default_data(db: Session):
db.add_all(default_templates)
db.commit()
print(f"✓ Created {len(default_templates)} default email templates")
# Seed default profile questions for onboarding and profile attributes
existing_questions = db.query(ProfileQuestion).count()
if existing_questions == 0:
print("Creating default profile questions...")
default_questions = [
ProfileQuestion(
key="has_professional_license",
label="Do you hold a professional aviation-related license?",
help_text="Select your current license status.",
input_type="select",
options_json=json.dumps([
{"label": "No", "value": "none"},
{"label": "Student", "value": "student"},
{"label": "Private Pilot", "value": "ppl"},
{"label": "Commercial Pilot", "value": "cpl"},
{"label": "ATPL", "value": "atpl"},
{"label": "Instructor", "value": "instructor"},
]),
is_required=False,
is_active=True,
admin_only_edit=False,
display_order=10,
),
ProfileQuestion(
key="license_number",
label="License number",
help_text="Optional: your current license number.",
input_type="text",
placeholder="e.g. UK.FCL.123456",
is_required=False,
is_active=True,
admin_only_edit=False,
display_order=20,
depends_on_value="ppl",
),
ProfileQuestion(
key="can_support_events",
label="Can you support airport or membership events?",
help_text="Choose yes if you're open to helping with events.",
input_type="boolean",
is_required=False,
is_active=True,
admin_only_edit=False,
display_order=30,
),
ProfileQuestion(
key="event_support_notes",
label="What support can you offer?",
help_text="Examples: stewarding, admin desk, setup/packdown, mentoring.",
input_type="text",
placeholder="Type details here",
is_required=False,
is_active=True,
admin_only_edit=False,
display_order=40,
depends_on_value="true",
),
ProfileQuestion(
key="hours_available_monthly",
label="Approximate volunteer hours available each month",
help_text="Optional estimate in hours.",
input_type="number",
is_required=False,
is_active=True,
admin_only_edit=False,
display_order=50,
),
ProfileQuestion(
key="medical_expiry_date",
label="Medical certificate expiry date",
help_text="Optional date in YYYY-MM-DD format.",
input_type="date",
is_required=False,
is_active=True,
admin_only_edit=False,
display_order=60,
),
ProfileQuestion(
key="completed_training_x",
label="Completed Training X",
help_text="This is set by admins once verified.",
input_type="boolean",
is_required=False,
is_active=True,
admin_only_edit=True,
display_order=70,
),
]
db.add_all(default_questions)
db.commit()
question_by_key = {question.key: question for question in db.query(ProfileQuestion).all()}
question_by_key["license_number"].depends_on_question_id = question_by_key["has_professional_license"].id
question_by_key["event_support_notes"].depends_on_question_id = question_by_key["can_support_events"].id
db.commit()
print(f"✓ Created {len(default_questions)} default profile questions")