Overview
The mobile app now ships first-party workspace surfaces for:- Drive at
Routes.drive - CRM at
Routes.crm - Education at
Routes.education
ShellMiniNavfor mode or section switchingShellChromeActionsfor quick actions such as filters, create, sort, and view toggles- A finance-style overview hero card inside the page body for stats and search
- Compact panel sections in the page body instead of raw admin
Card/ListTilestacks, so module pages read like first-class mobile products rather than direct dashboard ports
404 responses.
Mobile App Lock And Web QR Approval
The mobile app supports a device-level app lock from Settings > Session. The setting is stored in secure storage and uses the OS local authentication prompt with device PIN/passcode fallback allowed. When enabled, the app locks on an authenticated cold launch and after at least 30 seconds in the background. QR session handoff approval is gated by this setting:- The scanner lives at
Routes.settingsQrLoginScan. - The scanner only accepts
tuturuuu://auth/qr-loginpayloads from trusted Tuturuuu or local development origins. - The QR challenge must have been created by the same account that is active in the mobile app. Public unauthenticated QR sign-in is not supported because it would let an attacker poll a victim-approved challenge.
- Before approval, mobile runs local authentication and then calls
/api/v1/auth/qr-login/challenges/:id/approvewith the current Bearer session.
aal2:
- The web MFA screen creates an
/api/v1/auth/mfa/mobile/challengesrequest and shows a pair code. MobileMfaApprovalListeneris mounted at the app shell and polls/api/v1/auth/mfa/mobile/approvalswhile the user is authenticated, the app is in the foreground, and app lock is not covering the UI.- Pending pair codes open an app-level approval dialog. The Settings > Session tile remains a manual fallback surface for the same pending approvals.
- Mobile approves through
/api/v1/auth/mfa/mobile/challenges/:id/approve. - The web poller consumes the approved challenge and sets an HTTP-only
ttr_mfa_mobile_approvalcookie. The central auth proxy accepts that cookie only for the same user, the hidden web challenge secret, and an unexpired consumedmfa_mobile_approvalchallenge. - After a mobile TOTP challenge succeeds,
AuthCubit.verifyMfamust sync the refreshedaal2Supabase session into the active multi-account store. Without that sync, later mobile API calls can restore/send an olderaal1token and the approvals endpoint will respond withrequiresMobileMfa. - Multi-account removal and all-account logout must treat Supabase
auth.signOut()as required before clearing the local account store or emitting an unauthenticated Cubit state. Google/provider logout and stored refresh-token revocation can be best-effort, but a failed Supabase logout must leave the account visible with an error so the persisted session cannot silently reappear later. - Auth account models may serialize refresh tokens and session JSON only for the
encrypted secure-storage store. Do not include those raw secrets in
Equatable.props,toString, or other Cubit/Bloc state debug output because the global Bloc observer logs state changes during development.
Mobile Drive Coverage
The Drive screen inapps/mobile supports the main dashboard Drive flow:
- Workspace permission-gated loading
- Storage analytics summary
- Folder navigation and breadcrumb-style path movement
- Search, sort, grid/list toggle, and pagination
- Folder creation, rename, delete, bulk delete, and upload
- File preview, external open, share, and path copy
- Folder export-link generation and sharing
/api/v1/workspaces/:wsId/storage/list/api/v1/workspaces/:wsId/storage/analytics/api/v1/workspaces/:wsId/storage/folders/api/v1/workspaces/:wsId/storage/rename/api/v1/workspaces/:wsId/storage/object/api/v1/workspaces/:wsId/storage/share/api/v1/workspaces/:wsId/storage/export-links/api/v1/workspaces/:wsId/storage/upload-url/api/v1/workspaces/:wsId/storage/finalize-upload
Mobile CRM Coverage
The CRM screen inapps/mobile supports:
- Workspace permission-gated users and audit tabs
- Search, filters, pagination, and audit range filters
- Create, edit, and delete user records
- Avatar upload during create/edit flows
- Feedback create/update/delete flows
- Duplicate detection and merge flows
- CSV import with preview and deduping by email
- CSV export of the currently filtered users dataset
/api/v1/workspaces/:wsId/users/database/api/v1/workspaces/:wsId/users/api/v1/workspaces/:wsId/users/:userId/api/v1/workspaces/:wsId/users/groups/api/v1/workspaces/:wsId/users/feedbacks/api/v1/workspaces/:wsId/users/audit-logs/api/v1/workspaces/:wsId/users/duplicates/detect/api/v1/workspaces/:wsId/users/merge/api/v1/workspaces/:wsId/users/bulk-import/api/v1/workspaces/:wsId/users/avatar
Workspace not found even for valid workspace members.
Mobile Education Coverage
The Education screen inapps/mobile supports:
- An overview dashboard with counts for courses, quizzes, quiz sets, flashcards, and attempts
- Course search, create, edit, delete, and paginated browsing
- Library management for quizzes, quiz sets, and flashcards
- Quiz authoring with answer options, correctness flags, and explanations
- Attempt browsing with status and quiz-set filters plus attempt-detail review
apps/web:
/api/v1/workspaces/:wsId/courses/api/v1/workspaces/:wsId/courses/:courseId/api/v1/workspaces/:wsId/quizzes/api/v1/workspaces/:wsId/quizzes/:quizId/api/v1/workspaces/:wsId/quiz-sets/api/v1/workspaces/:wsId/quiz-sets/:setId/api/v1/workspaces/:wsId/flashcards/api/v1/workspaces/:wsId/flashcards/:flashcardId/api/v1/workspaces/:wsId/education/attempts/api/v1/workspaces/:wsId/education/attempts/:attemptId
.single() response on a collection endpoint can work in the web dashboard but break mobile Bearer-token callers or pagination assumptions.
When an Education mobile list needs derived counts from workspace-protected join tables such as course_module_quiz_sets, verify workspace membership first with the request-scoped client, then run the joined read on an admin-backed client. Also select real join columns such as module_id; do not assume those bridge tables expose a synthetic id.
Import And Export Format Notes
- The web CRM page still has richer spreadsheet tooling, including XLSX-oriented flows.
- Mobile import and export currently use CSV plus native share sheets.
- Keep the server contract for bulk import as a raw JSON array of
{ email, fullName }rows.
apps/mobile does not accept a stable XLSX package cleanly without conflicts, so mobile should prefer CSV/share flows unless spreadsheet generation is moved server-side later.
Verification
For Drive, CRM, or Education mobile changes:- Update
apps/mobile/lib/l10n/arb/*.arbfor every new user-facing string. - Run
flutter gen-l10n. - Run
bun check:mobile.