DutyBoost — Developer Wireframe

Full Stack Migration Guide v2.1

DutyBoost_Full_Stack_Development_Wireframe_v2.1.txt
================================================================================
  DUTYBOOST - FULL STACK DEVELOPMENT WIREFRAME & MIGRATION GUIDE
  For implementation outside Base44 (e.g. AWS)
  Version 2.1 | Prepared for internal engineering use
================================================================================

TABLE OF CONTENTS
  1.  System Overview
  2.  Technology Stack Recommendation (AWS)
  3.  Database Schema (all entities)
  4.  Authentication Architecture
  5.  Backend API Design (REST)
  6.  Core Business Logic
  7.  Frontend Architecture
  8.  Email Service
  9.  File Storage
  10. Role-Based Access Control (RBAC)
  11. POS Webhook Integration
  12. Environment Variables / Secrets
  13. Deployment Architecture (AWS)
  14. CI/CD Pipeline
  15. Key User Flows (End-to-End)
  16. Backend Functions Reference
  17. Migration Checklist


================================================================================
1. SYSTEM OVERVIEW
================================================================================

DutyBoost is a B2B2C SaaS travel retail platform. It connects three parties:
  - Travel Partners (agencies, hotels, car rentals, cruise lines, train operators)
  - Airport / Station / Port Retailers (duty-free shops, retail stores)
  - Travelers (end consumers)

Core value proposition:
  Travel partners generate 'DutyCredits' when they issue bookings.
  Travelers redeem DutyCredits at partner airport retailers for discounts.
  DutyBoost earns a 2.5% platform fee on each redemption transaction.
  Travel partners earn a commission of 20% of the DutyBoost fee on each redemption.

Business verticals (as of v2.0):
  1. Flight bookings (core) - generates Voucher / DutyCredit
  2. Hotel bookings         - generates Voucher
  3. Car rental bookings    - generates Voucher
  4. Cruise lines           - generates CruiseCredit
  5. Train / Rail operators - generates TrainCredit

DutyCredit validity: 30 days from the traveler's departure date.


================================================================================
2. TECHNOLOGY STACK RECOMMENDATION (AWS)
================================================================================

FRONTEND
  Framework:        React 18 + Vite
  Language:         JavaScript (or TypeScript)
  Styling:          Tailwind CSS + shadcn/ui
  State:            @tanstack/react-query (server state) + useState/useContext (local)
  Routing:          react-router-dom v6
  i18n:             i18next + react-i18next (6 languages: EN, NL, DE, FR, ES, IT)
  Charts:           Recharts
  QR Scanning:      jsQR (webcam-based scanning in-browser)
  Maps:             react-leaflet
  Drag & Drop:      @hello-pangea/dnd
  Animations:       framer-motion
  Hosting:          AWS CloudFront + S3 (static site)

BACKEND
  Runtime:          Node.js 20 (or Deno 2)
  Framework:        Express.js (or Hono for Deno/edge)
  Language:         JavaScript / TypeScript
  Hosting:          AWS Lambda + API Gateway (or ECS Fargate for persistent service)
  Database:         PostgreSQL on AWS RDS
  ORM:              Prisma (recommended) or Drizzle
  Auth:             AWS Cognito (or Auth.js / custom JWT)
  Email:            Postmark (already used) or AWS SES
  File Storage:     AWS S3
  QR Code:          api.qrserver.com (external, already in use) or sharp + qrcode npm
  Payments:         Mollie (already integrated - keep existing)
  Secret Management: AWS Secrets Manager or SSM Parameter Store
  Cron / Scheduled: AWS EventBridge Scheduler -> Lambda
  Business Registry: KVK API (Dutch Chamber of Commerce)


================================================================================
3. DATABASE SCHEMA (PostgreSQL)
================================================================================

All tables include: id (UUID PK), created_at, updated_at, created_by (user email)

--- users ---
  id            UUID PK
  email         VARCHAR UNIQUE NOT NULL
  full_name     VARCHAR
  role          VARCHAR  -- 'admin' | 'agency' | 'retailer' | 'user'
  created_at    TIMESTAMP
  updated_at    TIMESTAMP

--- signup_applications ---
  id                  UUID PK
  full_name           VARCHAR NOT NULL
  email               VARCHAR NOT NULL
  company             VARCHAR NOT NULL
  business_type       VARCHAR  -- 'Travel Partner' | 'Airport Retailer' | 'Train Partner'
  website             VARCHAR
  kvk                 VARCHAR
  vat                 VARCHAR
  booking_system      VARCHAR
  airport             VARCHAR
  store_category      VARCHAR
  travel_partners     TEXT[]   -- array of partner names
  status              VARCHAR  -- 'pending' | 'invited' | 'rejected'
  mollie_customer_id  VARCHAR
  mollie_payment_id   VARCHAR
  payment_method      VARCHAR
  bank_account_name   VARCHAR
  bank_iban           VARCHAR
  terms_accepted      BOOLEAN DEFAULT false
  created_at          TIMESTAMP
  updated_at          TIMESTAMP

--- bookings ---
  id                       UUID PK
  booking_id               VARCHAR NOT NULL
  total_ticket_price        DECIMAL(10,2)
  number_of_travelers       INTEGER
  travel_type               VARCHAR  -- 'Short-haul' | 'Long-haul'
  departure_airport         VARCHAR
  travel_date               DATE
  return_date               DATE
  agency_email              VARCHAR
  traveler_email            VARCHAR
  flight_reminder_disabled  BOOLEAN DEFAULT false
  created_at                TIMESTAMP
  updated_at                TIMESTAMP

--- vouchers ---
  id                  UUID PK
  voucher_code        VARCHAR UNIQUE NOT NULL  -- format: 'DB-XXXXXXXX'
  booking_id          VARCHAR NOT NULL
  discount_percentage DECIMAL(10,2)  -- stores euro credit amount, not a % despite name
  airport             VARCHAR
  expiration_date     DATE  -- travel_date + 30 days
  status              VARCHAR  -- 'active' | 'redeemed' | 'expired'
  traveler_count      INTEGER
  ticket_price        DECIMAL(10,2)
  travel_type         VARCHAR
  agency_email        VARCHAR
  traveler_email      VARCHAR  -- NOTE: also used by hotel / car-rental generated vouchers
  created_at          TIMESTAMP
  updated_at          TIMESTAMP

--- retailers ---
  id               UUID PK
  store_name       VARCHAR NOT NULL
  airport          VARCHAR NOT NULL
  category         VARCHAR  -- 'Retail'
  description      TEXT
  accepts_vouchers BOOLEAN DEFAULT true
  owner_email      VARCHAR
  created_at       TIMESTAMP
  updated_at       TIMESTAMP

--- transactions ---
  id                UUID PK
  voucher_code      VARCHAR NOT NULL
  retailer_id       UUID REFERENCES retailers(id)
  store_name        VARCHAR
  transaction_value DECIMAL(10,2)
  num_items         INTEGER
  discount_applied  DECIMAL(6,2)  -- effective % applied
  base_discount     DECIMAL(10,2)
  discount_amount   DECIMAL(10,2) -- euro saved by traveler
  platform_revenue  DECIMAL(10,2) -- 2.5% of gross, capped at EUR 25.00
  agency_commission DECIMAL(10,2) -- 20% of platform_revenue, capped at EUR 5.00
  agency_email      VARCHAR
  airport           VARCHAR
  created_at        TIMESTAMP
  updated_at        TIMESTAMP

--- airports ---
  id         UUID PK
  name       VARCHAR NOT NULL
  code       VARCHAR NOT NULL UNIQUE  -- IATA code e.g. AMS
  city       VARCHAR
  country    VARCHAR
  created_at TIMESTAMP
  updated_at TIMESTAMP

--- monthly_invoices ---
  id              UUID PK
  retailer_email  VARCHAR NOT NULL
  retailer_name   VARCHAR
  amount          DECIMAL(10,2)
  month_year      VARCHAR  -- 'YYYY-MM'
  invoice_date    DATE
  due_date        DATE
  status          VARCHAR  -- 'pending' | 'overdue' | 'paid'
  created_at      TIMESTAMP
  updated_at      TIMESTAMP

--- leads ---
  id              UUID PK
  company_name    VARCHAR NOT NULL
  website         VARCHAR
  industry        VARCHAR  -- 'Retail' | 'Agency'
  email           VARCHAR NOT NULL
  phone           VARCHAR
  full_name       VARCHAR NOT NULL
  position        VARCHAR
  decision_level  VARCHAR  -- 'Executive' | 'Manager' | 'Staff'
  created_at      TIMESTAMP
  updated_at      TIMESTAMP

--- survey_responses ---
  id                    UUID PK
  company_name          VARCHAR
  company_website       VARCHAR
  full_name             VARCHAR
  email                 VARCHAR
  phone                 VARCHAR
  company_type          VARCHAR
  annual_bookings       VARCHAR
  current_loyalty       VARCHAR
  interest_level        VARCHAR
  commission_attractive VARCHAR
  snippet_willingness   VARCHAR
  biggest_challenge     TEXT
  wants_demo            BOOLEAN DEFAULT false
  created_at            TIMESTAMP

--- travel_agencies ---
  id         UUID PK
  name       VARCHAR NOT NULL
  website    VARCHAR
  country    VARCHAR DEFAULT 'Netherlands'
  created_at TIMESTAMP
  updated_at TIMESTAMP

--- retailer_feedback ---
  id          UUID PK
  voucher_code VARCHAR
  airport      VARCHAR
  store_name   VARCHAR
  rating       INTEGER  -- 1-5 stars
  comment      TEXT
  created_at   TIMESTAMP
  updated_at   TIMESTAMP

--- account_deletions ---
  id          UUID PK
  email       VARCHAR NOT NULL
  user_type   VARCHAR  -- 'Airport Retailer' | 'Travel Partner'
  reason      VARCHAR NOT NULL
  other_reason TEXT
  created_at  TIMESTAMP
  updated_at  TIMESTAMP

--- transaction_aggregates ---
  id                UUID PK
  date              DATE NOT NULL
  retailer_id       UUID REFERENCES retailers(id)
  store_name        VARCHAR
  airport           VARCHAR
  transaction_count INTEGER
  total_volume      DECIMAL(10,2)
  total_discount    DECIMAL(10,2)
  platform_revenue  DECIMAL(10,2)
  created_at        TIMESTAMP
  updated_at        TIMESTAMP

--- terminal_movement_patterns ---
  id                  UUID PK
  airport             VARCHAR NOT NULL
  terminal            VARCHAR NOT NULL
  time_window         VARCHAR  -- e.g. '6:00-9:00 (Early Morning)'
  season              VARCHAR  -- 'Spring' | 'Summer' | 'Fall' | 'Winter' | etc.
  traffic_level       INTEGER  -- 1-10
  estimated_travelers INTEGER
  notes               TEXT
  submitted_by_email  VARCHAR
  is_agency           BOOLEAN DEFAULT false
  created_at          TIMESTAMP
  updated_at          TIMESTAMP


--- CRUISE MODULE ---

--- cruise_credits ---
  id              UUID PK
  token_id        VARCHAR UNIQUE NOT NULL
  traveler_email  VARCHAR
  traveler_name   VARCHAR
  cruise_line     VARCHAR
  home_port       VARCHAR
  balance         DECIMAL(10,2)
  remaining_balance DECIMAL(10,2)
  status          VARCHAR  -- 'active' | 'used' | 'expired' | 'partially_used'
  expiry          TIMESTAMP
  agency_email    VARCHAR
  created_at      TIMESTAMP
  updated_at      TIMESTAMP

--- cruise_transactions ---
  id              UUID PK
  token_id        VARCHAR NOT NULL
  store_name      VARCHAR
  retailer_id     UUID REFERENCES retailers(id)
  traveler_email  VARCHAR
  amount          DECIMAL(10,2)
  discount_amount DECIMAL(10,2)
  airport         VARCHAR
  status          VARCHAR  -- 'approved' | 'rejected'
  created_at      TIMESTAMP
  updated_at      TIMESTAMP


--- TRAIN / RAIL MODULE ---

--- train_credits ---
  id              UUID PK
  token_id        VARCHAR UNIQUE NOT NULL
  traveler_email  VARCHAR
  traveler_name   VARCHAR
  train_operator  VARCHAR
  departure_station VARCHAR
  balance         DECIMAL(10,2)
  remaining_balance DECIMAL(10,2)
  status          VARCHAR  -- 'active' | 'used' | 'expired' | 'partially_used'
  expiry          TIMESTAMP
  agency_email    VARCHAR
  created_at      TIMESTAMP
  updated_at      TIMESTAMP

--- train_transactions ---
  id              UUID PK
  token_id        VARCHAR NOT NULL
  store_name      VARCHAR
  retailer_id     UUID REFERENCES retailers(id)
  traveler_email  VARCHAR
  amount          DECIMAL(10,2)
  discount_amount DECIMAL(10,2)
  station         VARCHAR
  status          VARCHAR  -- 'approved' | 'rejected'
  created_at      TIMESTAMP
  updated_at      TIMESTAMP


================================================================================
4. AUTHENTICATION ARCHITECTURE
================================================================================

Recommended: AWS Cognito User Pools

  - Email/password login (primary)
  - Magic link login (already used via sendMagicLink function)
  - JWT access tokens with short TTL (1h) + refresh tokens (30 days)
  - User roles stored in Cognito custom attributes OR in your own DB users table

Token flow:
  Client -> POST /auth/login -> Server validates -> Returns JWT
  Client stores JWT in memory (not localStorage for security)
  Client sends JWT in Authorization: Bearer <token> header on every request
  Server middleware decodes + verifies JWT on every protected route

Role-based middleware pattern (Express.js):
  function requireRole(...roles) {
    return (req, res, next) => {
      const user = req.user; // set by JWT middleware
      if (!roles.includes(user.role)) return res.status(403).json({ error: 'Forbidden' });
      next();
    };
  }

User roles:
  admin     - full access to everything
  agency    - travel partner (can generate DutyCredits, view own transactions)
  retailer  - airport retailer (can scan/redeem vouchers, view own stats)
  user      - general (traveler-facing, limited access)

NOTE: Non-admin users are served a ComingSoonPage for all routes except surveys.


================================================================================
5. BACKEND API DESIGN (REST)
================================================================================

BASE URL: https://api.dutyboost.com/v1

All endpoints return JSON. Auth endpoints marked with [public].
All others require Authorization: Bearer <token> header.

--- AUTH ---
  POST   /auth/login              [public]  Email + password login
  POST   /auth/magic-link         [public]  Send magic link email
  POST   /auth/magic-link/verify  [public]  Verify magic link token -> return JWT
  POST   /auth/logout             Invalidate refresh token
  GET    /auth/me                 Return current user profile
  PATCH  /auth/me                 Update current user profile

--- VOUCHERS / DUTYCREDITS ---
  POST   /vouchers/generate       [agency|admin]  Generate DutyCredit from booking
    Body: { booking_id, total_ticket_price, number_of_travelers, travel_type,
            departure_airport, travel_date, return_date?, traveler_email, traveler_name? }
    Returns: { voucher_code, credit_amount_eur, expiration_date, qr_image_url }

  GET    /vouchers                [admin]         List all vouchers (paginated)
  GET    /vouchers/my             [agency]        List vouchers created by this agency
  GET    /vouchers/:code          [admin|agency]  Get single voucher by code
  POST   /vouchers/:code/expire   [admin]         Manually expire a voucher

--- POS / REDEMPTION ---
  POST   /pos/redeem              [public + POS secret header]
    Header: X-POS-Secret: <secret>
    Body: { voucher_code, transaction_value, retailer_id, store_name, airport }
    Returns: { success, discount_amount, amount_due, platform_fee, agency_commission }

  GET    /pos/validate/:code      [public + POS secret]  Check voucher status without redeeming

--- RETAILERS ---
  GET    /retailers               [admin]          List all retailers
  POST   /retailers               [admin]          Create retailer
  GET    /retailers/:id           [admin|retailer] Get retailer
  PATCH  /retailers/:id           [admin]          Update retailer
  DELETE /retailers/:id           [admin]          Delete retailer
  GET    /retailers/my            [retailer]       Get own retailer profile

--- TRANSACTIONS ---
  GET    /transactions            [admin]    All transactions (paginated, filterable)
  GET    /transactions/my         [agency]   Transactions for this agency's vouchers
  GET    /transactions/retailer   [retailer] Transactions at this retailer
  GET    /transactions/:id        [admin]    Single transaction

--- USERS ---
  GET    /users                   [admin]   List all users
  POST   /users/invite            [admin]   Invite user by email + role
  PATCH  /users/:id/role          [admin]   Change user role
  DELETE /users/:id               [admin]   Delete user

--- SIGNUP APPLICATIONS ---
  POST   /applications            [public]  Submit signup application
  GET    /applications            [admin]   List all applications
  POST   /applications/:id/approve [admin]  Approve + invite user
  POST   /applications/:id/reject  [admin]  Reject application
  DELETE /applications/:id        [admin]   Delete application

--- AIRPORTS ---
  GET    /airports                [public]  List all airports
  POST   /airports/import         [admin]   Bulk import airports

--- INVOICES ---
  GET    /invoices                [admin]   List all monthly invoices
  POST   /invoices/generate       [admin]   Generate invoices for a given month
  PATCH  /invoices/:id            [admin]   Update invoice status (paid/overdue)

--- ANALYTICS ---
  GET    /analytics/dashboard     [admin]   Platform-wide KPIs
  GET    /analytics/agency        [agency]  Agency-specific metrics
  GET    /analytics/retailer      [retailer] Retailer-specific metrics

--- CRUISE CREDITS ---
  POST   /cruise-credits/generate  [agency|admin]  Generate cruise credit
  GET    /cruise-credits/my        [agency]         List own cruise credits
  POST   /cruise-credits/:id/redeem [retailer]      Redeem cruise credit at port retailer

--- TRAIN CREDITS ---
  POST   /train-credits/generate   [agency|admin]  Generate train credit
  GET    /train-credits/my         [agency]         List own train credits
  POST   /train-credits/:id/redeem [retailer]       Redeem at station retailer

--- LEADS & SURVEYS ---
  POST   /leads                    [public]  Submit lead (GetInTouch form)
  POST   /surveys                  [public]  Submit survey response
  GET    /leads                    [admin]   List all leads
  GET    /surveys                  [admin]   List all survey responses
  DELETE /leads/cleanup            [admin]   Delete old leads (>90 days)

--- ACCOUNT DELETION ---
  POST   /account-deletion         [public]  Submit account deletion request
  GET    /account-deletion         [admin]   List deletion requests

--- FEEDBACK ---
  POST   /feedback/retailer        [public]  Submit retailer feedback (star + comment)
  GET    /feedback/retailer        [admin]   List all retailer feedback


================================================================================
6. CORE BUSINESS LOGIC
================================================================================

A. DutyCredit Calculation (flights)
------------------------------------
  Config:
    Short-haul base:  EUR 2.50 per traveler
    Long-haul base:   EUR 7.00 per traveler
    Floor:            EUR 5.00
    Cap:              EUR 60.00

  Multipliers (based on total ticket price):
    < EUR 200:   1.0x
    EUR 200-499: 1.5x
    EUR 500-999: 2.0x
    >= EUR 1000: 2.5x

  Formula:
    raw = basePerTraveler * numTravelers * multiplier
    creditAmount = clamp(raw, floor=5, cap=60)

  Expiration:
    expirationDate = travelDate + 30 days

  Voucher code format: 'DB-' + 8 random alphanumeric chars (e.g. DB-A3X7KQ2P)

B. Redemption Tiers (applied at POS for standard vouchers)
----------------------------------------------------------
  EUR 70  - 99  -> Boarding DutyCredit:  EUR 10 discount
  EUR 100 - 199 -> Priority DutyCredit:  EUR 30 discount
  EUR 200 - 299 -> Lounge DutyCredit:    EUR 45 discount
  >= EUR 300    -> VIP DutyCredit:       EUR 60 discount
  < EUR 70      -> Not eligible (minimum spend)

  Financial split per transaction:
    Platform revenue (DutyBoost fee): 2.5% of gross transaction value, capped at EUR 25.00
    Agency commission:                 20% of DutyBoost fee (capped at EUR 5.00 when fee is capped)
    Retailer pays:                     discountAmount to traveler

C. Voucher Lifecycle
--------------------
  active -> redeemed  (after successful POS scan)
  active -> expired   (if expiration_date < today when scanned)
  Manual: admin can force-expire a voucher

D. Invoice Generation (monthly)
-------------------------------
  Run on the 1st of each month via cron.
  For each retailer: sum all transactions from prior month.
  Create MonthlyInvoice record with amount = total platform_revenue.
  Note: platform_revenue = 2.5% of transaction_value, capped at EUR 25.00 per transaction.
  due_date = invoice_date + 14 days. Send invoice email to retailer.

E. Agency Payouts (monthly)
---------------------------
  Run on the 1st of each month via cron.
  For each agency: sum all agency_commission from prior month's transactions.
  Note: agency_commission = 20% of platform_revenue per transaction (capped at EUR 5.00/tx).
  Process payout via Mollie API (bank transfer to agency IBAN).

F. Flight Reminders
-------------------
  Run daily via cron.
  Find bookings where travel_date = tomorrow AND flight_reminder_disabled = false.
  Send reminder email to traveler_email with their voucher code + QR.

H. Cruise Credit Logic
----------------------
  CruiseCredit generated at booking time by cruise line partner.
  token_id = unique credit identifier sent to traveler.
  Redeemable at participating port/duty-free retailers.
  Balance is partial-use (remaining_balance decreases per transaction).

I. Train Credit Logic
---------------------
  TrainCredit generated at booking time by train/rail operator.
  Redeemable at participating station retailers before boarding.
  Same partial-balance model as CruiseCredit.


================================================================================
7. FRONTEND ARCHITECTURE
================================================================================

Directory Structure:
  /src
  |-- api/
  |   +-- base44Client.js       # SDK client (replace with your own Axios client)
  |-- pages/
  |   |-- LandingPage.jsx       # Public landing page
  |   |-- ComingSoonPage.jsx    # Default for non-admin users
  |   |-- SelectUserType.jsx    # Partner type selection (5 verticals)
  |   |-- GetStarted.jsx        # Legacy signup application form
  |   |-- AgencyPortal.jsx      # Travel partner dashboard
  |   |-- AgencySignup.jsx      # Travel partner signup wizard
  |   |-- AgencyShowcase.jsx    # Travel partner showcase/marketing
  |   |-- RetailerPortal.jsx    # Retailer dashboard
  |   |-- RetailerSignup.jsx    # Retailer signup wizard
  |   |-- RetailerShowcase.jsx  # Retailer showcase/marketing
  |   |-- RetailerScanQR.jsx    # QR scanner for redemptions
  |   |-- RetailerScanOnly.jsx  # Simplified scan-only mode
  |   |-- HotelPortal.jsx       # Hotel partner dashboard
  |   |-- HotelSignup.jsx       # Hotel signup wizard
  |   |-- HotelShowcase.jsx     # Hotel showcase/marketing
  |   |-- CarRentalPortal.jsx   # Car rental dashboard
  |   |-- CarRentalSignup.jsx   # Car rental signup wizard
  |   |-- CarRentalShowcase.jsx # Car rental showcase/marketing
  |   |-- CruisePortal.jsx      # Cruise line dashboard
  |   |-- CruiseSignup.jsx      # Cruise signup wizard
  |   |-- CruiseShowcase.jsx    # Cruise showcase/marketing
  |   |-- TrainPortal.jsx       # Train operator dashboard
  |   |-- TrainSignup.jsx       # Train signup wizard
  |   |-- TrainShowcase.jsx     # Train showcase/marketing
  |   |-- AdminDashboard.jsx    # Admin analytics
  |   |-- AdminPanel.jsx        # User/app management
  |   |-- ScanVoucher.jsx       # Voucher scan/lookup
  |   |-- AirportMap.jsx        # Interactive airport map (react-leaflet)
  |   |-- DemoLanding.jsx       # Demo mode landing
  |   |-- DemoAgencyDashboard.jsx
  |   |-- DemoRetailerDashboard.jsx
  |   |-- DemoHotelDashboard.jsx
  |   |-- DemoCarRentalDashboard.jsx
  |   |-- DemoCruiseDashboard.jsx
  |   |-- DemoTrainDashboard.jsx
  |   |-- DemoAdminDashboard.jsx
  |   |-- SurveyDashboard.jsx   # Admin view of survey responses
  |   |-- LeadsAdmin.jsx        # Admin view of leads
  |   |-- FAQ.jsx
  |   |-- TermsOfService.jsx
  |   |-- PrivacyPolicy.jsx
  |   |-- RefundPolicy.jsx
  |   |-- CookiePolicy.jsx
  |   |-- DataProcessingAgreement.jsx
  |   |-- CodeOfConduct.jsx
  |   |-- UserGuide.jsx
  |   |-- AgencyIntegrationGuide.jsx
  |   |-- AgencyIntegrationPage.jsx
  |   |-- RetailerIntegrationGuide.jsx
  |   |-- AgencyOnePager.jsx
  |   |-- RetailerOnePager.jsx
  |   |-- RetailerPresentationFuture.jsx
  |   |-- TravelPartnerPresentationFuture.jsx
  |   |-- RetailerCalculator.jsx
  |   |-- RegistrationFlow.jsx
  |   |-- EmailPreview.jsx
  |   |-- GTMTestingChecklist.jsx
  |   |-- StagingValidationGuide.jsx
  |   |-- DevWireframe.jsx      # This document viewer
  |   |-- TravelPartnerSurvey.jsx         (+ NL, DE, FR, ES, IT variants)
  |   +-- AirportRetailerSurvey.jsx       (+ NL, DE, FR, ES, IT variants)
  |-- components/
  |   |-- Layout.jsx              # Sidebar + nav shell
  |   |-- DutyBoostLogo.jsx
  |   |-- BookingForm.jsx         # Generate DutyCredit form
  |   |-- VoucherCard.jsx
  |   |-- QRScanner.jsx           # jsQR-based scanner
  |   |-- QRCodeDisplay.jsx
  |   |-- PageFooter.jsx
  |   |-- FooterLinks.jsx
  |   |-- LanguageSwitcher.jsx
  |   |-- CookieConsentBar.jsx
  |   |-- WeCallYouModal.jsx      # 'We call you' lead capture modal
  |   |-- WeCallYouForm.jsx
  |   |-- GetInTouchModal.jsx
  |   |-- GetInTouchForm.jsx
  |   |-- KvkValidator.jsx        # Dutch Chamber of Commerce validator
  |   |-- ScrollRestoration.jsx
  |   |-- TravelerIcon.jsx        # Icon components for each vertical
  |   |-- TravelAgencyIcon.jsx
  |   |-- AirportRetailerIcon.jsx
  |   |-- HotelIcon.jsx
  |   |-- CarRentalIcon.jsx
  |   |-- CruiseShipIcon.jsx
  |   |-- TrainIcon.jsx
  |   +-- ui/                    # shadcn/ui components (keep as-is)
  |-- lib/
  |   |-- AuthContext.jsx         # Replace Base44 auth with your own JWT auth
  |   |-- i18n.js                 # i18next config
  |   |-- i18nTraveler.js         # Traveler-facing i18n config
  |   |-- discountCalculator.js   # calcBasketCredit() pure function
  |   +-- query-client.js         # @tanstack/react-query client instance
  |-- locales/
  |   |-- en.js
  |   |-- nl.js
  |   |-- de.js
  |   |-- fr.js
  |   |-- es.js
  |   +-- it.js
  |-- utils/
  |   |-- encryptionUtils.js      # Field-level encryption for PII in applications
  |   |-- exportToPptx.js         # PowerPoint export utility
  |   |-- exportRetailerPptx.js
  |   +-- exportTravelPartnerPptx.js
  |-- data/
  |   |-- demoData.js             # Mock data for demo dashboards
  |   +-- devGuideContent.js      # This document
  +-- App.jsx                    # React Router config

Route Structure (App.jsx):
  /                     -> LandingPage (authenticated admin) / Navigate to /dashboard
  /get-started          -> SelectUserType (5 partner types)
  /travel-partner-signup -> AgencySignup (4-step wizard: info -> bank -> PO -> terms)
  /hotel-signup          -> HotelSignup (2-step: info -> terms)
  /car-rental-signup     -> CarRentalSignup (2-step: info -> terms)
  /cruise-signup         -> CruiseSignup (2-step: info -> terms)
  /train-signup          -> TrainSignup (2-step: info -> terms)
  /retailer-signup       -> RetailerSignup (3-step: info -> payment -> terms)
  /travel-partner-portal -> AgencyPortal
  /hotel-portal          -> HotelPortal
  /car-rental-portal     -> CarRentalPortal
  /cruise-portal         -> CruisePortal
  /train-portal          -> TrainPortal
  /retailer-portal       -> RetailerPortal
  /demo                  -> DemoLanding
  /demo/travel-partner   -> DemoAgencyDashboard
  /demo/retailer         -> DemoRetailerDashboard
  /demo/hotel            -> DemoHotelDashboard
  /demo/car-rental       -> DemoCarRentalDashboard
  /demo/cruise           -> DemoCruiseDashboard
  /demo/train            -> DemoTrainDashboard
  /demo/admin            -> DemoAdminDashboard
  /travel-partner-survey (+ /nl /de /fr /es /it variants)
  /airport-port-retailer-survey (+ /nl /de /fr /es /it variants)
  Public survey routes (no auth required):
    /travel-partner-survey, /travel-partner-survey-nl/de/fr/es/it
    /airport-port-retailer-survey, /airport-port-retailer-survey-nl/de/fr/es/it

  Layout-wrapped routes (require auth, admin only):
    /dashboard, /vouchers, /retailers, /travel-partners
    /admin, /admin-dashboard, /retailer-dashboard
    /scan-redeem, /retailer-scan, /leads-admin, /survey-dashboard

API Client Pattern (replacing Base44 SDK):
  // src/api/client.js
  import axios from 'axios';
  const api = axios.create({ baseURL: 'https://api.dutyboost.com/v1' });
  api.interceptors.request.use(config => {
    const token = getToken(); // from memory/context
    if (token) config.headers.Authorization = 'Bearer ' + token;
    return config;
  });
  export default api;

Replace Base44 entity calls with:
  base44.entities.Voucher.list()            ->  api.get('/vouchers')
  base44.entities.Voucher.create(data)      ->  api.post('/vouchers/generate', data)
  base44.entities.Transaction.filter({})   ->  api.get('/transactions')
  base44.entities.CruiseCredit.create(data) ->  api.post('/cruise-credits/generate', data)
  base44.entities.TrainCredit.create(data)  ->  api.post('/train-credits/generate', data)
  base44.auth.me()                          ->  api.get('/auth/me')


================================================================================
8. EMAIL SERVICE
================================================================================

Provider: Postmark (already in use - keep same credentials)

Emails sent by the platform:
  1. DutyCredit email       - traveler receives QR code + discount info
  2. Welcome email          - new partner onboarded after approval
  3. Magic link             - passwordless login
  4. Flight reminder        - day before departure
  5. Monthly invoice        - sent to retailers
  6. Voucher redemption     - confirmation to agency
  7. Lead confirmation      - auto-reply when lead submits form
  8. Commission alert       - notify agency of large commission earned
  9. Signup welcome         - sent after application approved + invited
  10. Purchase order email  - sent to partner when PO is generated

Postmark integration:
  npm install postmark
  import { ServerClient } from 'postmark';
  const client = new ServerClient(process.env.POSTMARK_API_KEY);
  await client.sendEmail({
    From: 'hello@dutyboost.com',
    To: recipientEmail,
    Subject: subject,
    HtmlBody: htmlBody,
  });


================================================================================
9. FILE STORAGE (AWS S3)
================================================================================

Buckets:
  dutyboost-public      - logos, public assets (CloudFront CDN)
  dutyboost-private     - signed documents, invoices (no public access)

S3 SDK usage:
  import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
  import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
  const s3 = new S3Client({ region: 'eu-west-1' });

  // Upload file
  await s3.send(new PutObjectCommand({
    Bucket: 'dutyboost-private',
    Key: 'invoices/' + invoiceId + '.pdf',
    Body: pdfBuffer,
    ContentType: 'application/pdf',
  }));

  // Generate short-lived download URL (300 seconds)
  const url = await getSignedUrl(s3, new GetObjectCommand({
    Bucket: 'dutyboost-private',
    Key: 'invoices/' + invoiceId + '.pdf',
  }), { expiresIn: 300 });

Encrypted fields:
  signup_applications.bank_iban, bank_account_name are encrypted client-side
  using AES-256-GCM before storage (see utils/encryptionUtils.js).
  Decryption only via getSignupApplicationDecrypted backend function.


================================================================================
10. ROLE-BASED ACCESS CONTROL (RBAC)
================================================================================

Role           Permissions
-----------------------------------------------------------------------------
admin          All endpoints. Manage users, applications, invoices, analytics.
agency         Generate DutyCredits. View own bookings, vouchers, transactions.
               View own commission data.
retailer       Scan QR codes (POS redemption). View own transactions and stats.
               Access own retailer profile.
user (public)  Submit applications, surveys, contact forms. No dashboard access.
               Routed to ComingSoonPage for all app routes.
-----------------------------------------------------------------------------

Row-level data filtering:
  - agency users: always filter transactions/vouchers by agency_email = currentUser.email
  - retailer users: always filter transactions by retailer_id = currentUser.retailerId
  - admin: no filter, sees everything

RLS rules on sensitive entities:
  Transaction: read allowed if data.agency_email = user.email OR user.role = admin
  Lead: read/update/delete requires admin role

Data the POS webhook reads without a user login (authenticated by shared secret):
  - Voucher lookup and update
  - Transaction creation


================================================================================
11. POS WEBHOOK INTEGRATION
================================================================================

Endpoint: POST /pos/redeem
Authentication: Shared secret in header  X-POS-Secret: <secret>
               (retailer receives this during onboarding)

Request body:
  {
    "voucher_code":       "DB-A3X7KQ2P",
    "transaction_value":  150.00,
    "retailer_id":        "uuid-of-retailer",
    "store_name":         "World Duty Free - Gate B22",
    "airport":            "AMS"
  }

Success response:
  {
    "success": true,
    "transaction_id": "uuid",
    "voucher_code": "DB-A3X7KQ2P",
    "gross_value": 150.00,
    "effective_discount_pct": 20.00,
    "discount_amount": 30.00,
    "amount_due": 120.00,
    "platform_fee": 3.75,       // 2.5% of gross, max EUR 25.00
    "agency_commission": 0.75,  // 20% of platform_fee, max EUR 5.00
    "message": "DutyCredit applied. Traveler saves EUR 30.00."
  }

Error scenarios:
  401 - Invalid POS secret
  404 - Voucher not found
  409 - Voucher already redeemed or expired
  400 - Transaction value below EUR 70 minimum

Additional POS-style endpoints:
  POST /cruise/redeem   - Cruise credit redemption at port retailer
  POST /train/redeem    - Train credit redemption at station retailer
  All follow same authentication pattern with appropriate shared secrets.


================================================================================
12. ENVIRONMENT VARIABLES / SECRETS
================================================================================

Store all secrets in AWS Secrets Manager or SSM Parameter Store.
Never commit secrets to source control.

Required environment variables:
  DATABASE_URL           PostgreSQL connection string
  JWT_SECRET             Secret for signing JWT tokens (min 32 chars)
  POSTMARK_API_KEY       Postmark server token (env var: PostMark)
  MOLLIE_API_KEY         Mollie live API key (for payments and payouts)
  POS_WEBHOOK_SECRET     Shared secret distributed to POS retailers
  KVK_API_KEY            Dutch Chamber of Commerce validation API
  ENCRYPTION_KEY         AES-256 key for encrypting PII fields (bank details)
  AWS_REGION             e.g. eu-west-1
  AWS_S3_BUCKET_PUBLIC   dutyboost-public
  AWS_S3_BUCKET_PRIVATE  dutyboost-private
  FRONTEND_URL           https://app.dutyboost.com (for CORS)
  FROM_EMAIL             hello@dutyboost.com

Optional / feature flags:
  QR_SERVICE_URL         https://api.qrserver.com/v1/create-qr-code (or self-hosted)
  ENABLE_CRUISE          true|false
  ENABLE_TRAIN           true|false


================================================================================
13. DEPLOYMENT ARCHITECTURE (AWS)
================================================================================

  [Browser]
      |
      +---- HTTPS ---> [CloudFront CDN]
      |                    |
      |              +-----+-------+
      |              |             |
      |         [S3 bucket]    [API Gateway]
      |      (React app files)      |
      |                        [Lambda / ECS]
      |                        (Node.js API)
      |                             |
      |                    +--------+--------+
      |                    |                 |
      |               [RDS PostgreSQL]   [S3 Private]
      |               (eu-west-1)        (invoices etc.)
      |
      +---- POS Terminal ---> [API Gateway /pos/redeem]

Recommended AWS services:
  Compute:      AWS Lambda (API) or ECS Fargate (if persistent connections needed)
  Database:     RDS PostgreSQL (Multi-AZ for production)
  CDN/Hosting:  CloudFront + S3
  Auth:         AWS Cognito or custom JWT
  Scheduler:    EventBridge Scheduler (for cron jobs)
  Secrets:      AWS Secrets Manager
  Monitoring:   CloudWatch + X-Ray
  DNS:          Route 53
  SSL:          AWS Certificate Manager (ACM)

Recommended regions: eu-west-1 (Ireland) - closest to Dutch/European users

Domain structure:
  app.dutyboost.com       -> Frontend (CloudFront + S3)
  api.dutyboost.com       -> Backend API (API Gateway + Lambda)
  dutyboost.com           -> Landing page / marketing


================================================================================
14. CI/CD PIPELINE
================================================================================

Recommended: GitHub Actions

Frontend pipeline (on push to main):
  1. npm ci
  2. npm run build
  3. aws s3 sync ./dist s3://dutyboost-public --delete
  4. aws cloudfront create-invalidation --distribution-id XXXXX --paths '/*'

Backend pipeline (on push to main):
  1. npm ci
  2. Run tests (jest or vitest)
  3. Deploy Lambda: serverless deploy or AWS CDK deploy
  4. Run DB migrations: npx prisma migrate deploy

Environments:
  staging    -> staging.dutyboost.com / staging-api.dutyboost.com
  production -> app.dutyboost.com / api.dutyboost.com

Branch strategy:
  main       -> production deploy
  develop    -> staging deploy
  feature/*  -> PR -> develop


================================================================================
15. KEY USER FLOWS (END-TO-END)
================================================================================

FLOW 1: Travel Partner generates a DutyCredit (flight)
-------------------------------------------------------
  1. Agency user logs in -> JWT issued
  2. Opens BookingForm component
  3. Fills in: booking_id, ticket price, # travelers, travel type,
     departure airport, travel date, traveler email
  4. Submits -> POST /vouchers/generate
  5. Backend: calculateDutyCredit() -> generate voucher code (DB-XXXXXXXX)
              -> set expiration = travelDate + 30 days
              -> save Booking + Voucher to DB
              -> generate QR image URL
              -> send DutyCredit email to traveler via Postmark
  6. Frontend: shows success + voucher code to agency user

FLOW 2: Traveler redeems DutyCredit at airport
-----------------------------------------------
  1. Traveler arrives at participating store, shows QR on phone
  2. Cashier scans QR on POS terminal
  3. POS sends POST /pos/redeem with voucher_code + basket value
  4. Backend: validates secret -> looks up voucher -> checks status + expiry
              -> calcBasketCredit(basketValue) -> calculates financials
              -> platformFee = min(grossValue * 0.025, 25.00)
              -> agencyCommission = platformFee * 0.20
              -> creates Transaction record -> marks Voucher as 'redeemed'
              -> returns discount breakdown to POS
  5. POS applies discount, prints receipt showing EUR X saved

FLOW 3: New partner onboarding
--------------------------------
  1. Partner visits /get-started -> chooses their vertical (5 options)
  2. Fills signup form for their type (AgencySignup, HotelSignup, etc.)
  3. Application saved to signup_applications (POST /applications)
  4. Admin reviews in admin panel -> approves (POST /applications/:id/approve)
  5. Backend: sends invite email with magic link
  6. Partner clicks link -> authenticated -> role set
  7. Partner can now access their portal

FLOW 4: Monthly retailer billing
---------------------------------
  1. EventBridge Scheduler triggers cron on 1st of month
  2. Lambda POST /invoices/generate runs:
     - Sums transactions per retailer for previous month
     - Creates MonthlyInvoice records
     - Sends invoice emails via Postmark
  3. Retailer pays via Mollie payment link
  4. Mollie webhook -> update invoice status to 'paid'

FLOW 5: Agency commission payout
---------------------------------
  1. EventBridge Scheduler triggers cron on 1st of month
  2. Lambda aggregates agency_commission per agency
  3. Initiates Mollie bank transfer to agency IBAN
  4. Logs payout in system

FLOW 6: Cruise credit redemption at port
------------------------------------------
  1. Cruise line generates CruiseCredit at booking
  2. Traveler arrives at port duty-free store
  3. Retailer scans token -> POST /cruise/redeem
  4. CruiseTransaction created, credit balance reduced

FLOW 7: Lead capture and follow-up
------------------------------------
  1. Potential partner fills GetInTouch/WeCallYou form
  2. Lead saved to leads table (POST /leads)
  3. Auto-reply email sent via sendLeadConfirmationEmail
  4. Admin reviews leads in /leads-admin
  5. Weekly cleanup removes leads older than 90 days


================================================================================
16. BACKEND FUNCTIONS REFERENCE
================================================================================

All backend functions run as serverless handlers (Deno-based on Base44, Lambda on AWS).

Function                        Trigger         Description
------------------------------------------------------------------------------
generateDutyCredit              HTTP POST       Generate flight DutyCredit voucher
generateTrainCredit             HTTP POST       Generate train credit token
posWebhook                      HTTP POST       POS redemption webhook handler
approveApplication              HTTP POST       Approve signup + invite user
autoApproveApplication          HTTP POST       Auto-approve with role assignment
createSignupApplicationEncrypted HTTP POST      Create application with encrypted PII
getSignupApplicationDecrypted   HTTP GET        Retrieve application with decrypted PII
inviteUser                      HTTP POST       Invite user by email + role
adminSetUserRole                HTTP POST       Change user role (admin only)
assignRoleOnUserCreate          Entity trigger  Auto-assign default role on user create
ensureUserRole                  HTTP POST       Ensure user has correct role
ensureBothAdmins                HTTP POST       Ensure both admin accounts exist
fixUserRole                     HTTP POST       Repair corrupted user role
createTestAdmin                 HTTP POST       Create test admin user (dev only)
sendSignupWelcomeEmail          HTTP POST       Send welcome email to new partner
sendLeadConfirmationEmail       HTTP POST       Auto-reply to lead form submissions
sendMagicLink                   HTTP POST       Send passwordless magic link
sendFlightReminders             Scheduled daily Find tomorrow's departures + email travelers
sendRedemptionNotification      HTTP POST       Notify agency of voucher redemption
sendCommissionAlert             HTTP POST       Alert agency of large commission
sendPurchaseOrderEmail          HTTP POST       Email purchase order to partner
notifyVoucherRedeemed           Entity trigger  Trigger on Voucher status -> redeemed
sendTestEmail                   HTTP POST       Send test email (dev/admin only)
generateMonthlyInvoices         Scheduled 1st  Generate retailer invoices for prior month
generateMonthlyRetailerPayouts  Scheduled 1st  Process retailer payouts
generateMonthlyAgencyPayouts    Scheduled 1st  Process agency commission payouts
executeAgencyPayouts            HTTP POST       Manual trigger for agency payouts
executeBatchPayouts             HTTP POST       Batch payout execution
setupAgencyPayout               HTTP POST       Configure Mollie payout for agency
checkOverdueInvoices            Scheduled daily Mark overdue invoices
generateDailyMetrics            Scheduled daily Aggregate daily transaction metrics
createMolliePayment             HTTP POST       Create Mollie payment session
mollieInvoiceWebhook            HTTP POST       Handle Mollie payment webhook
mollieCardManager               HTTP POST       Manage Mollie card payment methods
processRetailerCardPayment      HTTP POST       Process retailer card payment
validateKvk                     HTTP POST       Validate KVK number via Dutch API
importAirports                  HTTP POST       Bulk import airport data
cleanupOldLeads                 Scheduled weekly Delete leads older than 90 days
deleteAllApplications           HTTP POST       Delete all applications (admin/dev only)
deleteUserOnApplicationDelete   Entity trigger  Clean up user when application deleted
gmailAutoReply                  Connector       Auto-reply to inbound Gmail messages
------------------------------------------------------------------------------

Scheduled function cron schedule:
  sendFlightReminders        -> daily 07:00 UTC
  checkOverdueInvoices       -> daily 08:00 UTC
  generateDailyMetrics       -> daily 02:00 UTC
  generateMonthlyInvoices    -> 1st of month 06:00 UTC
  generateMonthlyRetailerPayouts -> 1st of month 07:00 UTC
  generateMonthlyAgencyPayouts   -> 1st of month 08:00 UTC
  cleanupOldLeads            -> weekly Monday 03:00 UTC


================================================================================
17. AUDIT FIXES (v2.0 Updates)
================================================================================

Critical bugs fixed in current build:

BUG 1: Missing translation keys in signup forms
  Issue: AgencySignup & RetailerSignup referenced non-existent i18n keys
         (e.g., t('getstarted.full_name') -> undefined label)
  Fix: Hardcoded English labels directly in form components

BUG 2: Broken sign-in redirects
  Issue: AgencySignup redirected to /agency-portal (non-existent route)
  Fix: Changed to /travel-partner-portal (correct route)

BUG 3: Homepage routing logic
  Issue: Authenticated admin users still saw LandingPage; non-admins got ComingSoonPage
  Fix: Home route now checks user?.role === 'admin'; non-admin redirects to /dashboard

BUG 4: Survey Dashboard missing imports
  Issue: SurveyDashboard used FileText icon without importing
  Fix: Added FileText to lucide-react import

BUG 5: Undefined variable in App.jsx
  Issue: Lint error - 'authedUser' is not defined
  Fix: Changed to 'user' (correct destructured variable from useAuth)

Feature: Fee cap enforcement (v2.0)
  - Platform fee capped at EUR 25.00 per transaction (2.5% of gross, max EUR 25)
  - Agency commission capped at EUR 5.00 per transaction (20% of platform fee)
  - Cap applied in: posWebhook, generateMonthlyInvoices, generateMonthlyAgencyPayouts,
    executeAgencyPayouts, RetailerCalculator, SurveyDashboard RevenueCalculator,
    DemoAdminDashboard, RetailerDashboard

Feature: Survey Dashboard enhancements (v2.0)
  - Added expandable response cards showing full survey answers per respondent
  - Added CSV + Excel export for all responses
  - Added individual response download buttons (CSV/Excel per response)
  - Searchable survey data with language detection flags
  - Supports: Travel Partner & Airport Retailer surveys in 6 languages (EN, NL, DE, FR, ES, IT)

Role consistency notes:
  - 'agency' role in code = 'Travel Partner' in UI (display via getRoleLabel())
  - Terminology: 'DutyCredit' for flights, 'CruiseCredit' for cruises, 'TrainCredit' for trains
  - Non-admin users routed to ComingSoonPage for all routes except public surveys


================================================================================
18. MIGRATION CHECKLIST (Base44 -> AWS)
================================================================================

  [ ] Export all production data from Base44 (CSV/JSON for all entities)
  [ ] Set up RDS PostgreSQL with full schema (section 3)
  [ ] Import data into PostgreSQL tables
  [ ] Build Express.js API with all endpoints in section 5
  [ ] Replace base44.auth.me() with JWT middleware
  [ ] Replace base44.entities.X.list() with API calls
  [ ] Replace base44.integrations.Core.SendEmail() with Postmark SDK
  [ ] Port encryption logic (utils/encryptionUtils.js) to backend
  [ ] Set up AWS Cognito (or JWT) auth
  [ ] Set up S3 buckets
  [ ] Deploy backend to Lambda (or ECS)
  [ ] Deploy frontend to S3 + CloudFront
  [ ] Configure EventBridge cron jobs for all scheduled functions (section 16)
  [ ] Configure custom domains + SSL
  [ ] Move all secrets to AWS Secrets Manager
  [ ] Migrate Mollie customer/payment IDs to new system
  [ ] Configure POS webhook secret for all active retailers
  [ ] Run end-to-end test: generate DutyCredit -> redeem at POS
  [ ] Test cruise credit flow end-to-end
  [ ] Test train credit flow end-to-end
  [ ] Enable monitoring (CloudWatch alarms on Lambda errors)
  [ ] Verify i18n works for all 6 languages (EN, NL, DE, FR, ES, IT)

================================================================================
APPENDIX: KNOWN LIMITATIONS & TECH DEBT (v2.0)
================================================================================

Frontend Maintenance (post-launch items):
  - SurveyDashboard.jsx is 865+ lines; consider splitting into sub-components
  - Signup wizard components (AgencySignup, RetailerSignup, etc.) repeat code
    -> Refactor: create reusable <SignupWizard> wrapper component
  - Translation keys scattered across multiple locale files (currently 6 languages)
    -> Best practice: organize by page/section rather than flat key names

Backend Functions (v2.0):
  - All backend functions currently Deno-based (Base44 platform)
  - Migration to AWS Lambda will require: Node.js 20 + Express/Hono adaption
  - POS webhook secret distribution needs secure channel setup

Security considerations:
  - Bank IBAN/account names encrypted client-side before transmission
  - PII (name, email, phone) not encrypted at rest (consider client-side encryption for sensitive apps)
  - Survey responses stored with language flags for audit trail
  - All RLS rules enforced at database layer (see section 10)

Performance notes:
  - Survey Dashboard loads up to 500 responses at once (max pagination)
    -> Consider lazy-loading if dataset grows >1000 records
  - QR codes generated server-side (via external QR service or sharp)
  - Chart rendering with Recharts; consider virtualization if 1000+ data points


================================================================================
  END OF DOCUMENT
  DutyBoost Development Wireframe v2.1 (Updated May 2026)
  Includes audit fixes, survey dashboard updates, and v2.0 architectural notes

================================================================================

We use cookies to improve your experience. You decide which cookies to allow — essential cookies are always required for the platform to function. Cookie Policy