The UltraFit360 Coach portal is a separate React + TypeScript web application that gives trainers a programme builder, member messaging and analytics view alongside the consumer app. The architectural insight: it shares the same Firestore database as the mobile app rather than having its own.
Why share the database
When a coach edits a programme, their members see it on next sync. When a member finishes a workout, it appears on the coach's dashboard immediately. Splitting the database would have meant a sync layer between them — extra surface area, extra bugs. Sharing it means every write is honest in real time.
How we scope access
Firestore security rules enforce that a coach can only read their own roster. Each coach has a document at /coaches/{coachId}, and each member document carries the coach reference so security rules can chain the check. Role-based privileged writes (creating a programme, moving a member between coaches) go through Cloud Functions so the rule surface stays small.
Conflict resolution
Two coaches editing the same shared programme would have stepped on each other. The portal uses Firestore transactions on every programme write — read the current version, validate it hasn't changed, write the new one. If a transaction loses, the coach sees a "this programme was updated elsewhere" toast and refreshes.
What's on the roadmap
- Member messaging — already in alpha, currently text-only
- AI-drafted programme generation grounded in the member's recent data
- Stripe Connect for member-to-coach payments
- A coach-facing analytics dashboard with cohort retention
The trade-off of a shared database
Sharing one Firestore between the consumer app and the coach portal is not free. It means a schema change has to satisfy two clients at once, and a careless security rule could leak one coach's roster to another. We accept that cost because the alternative — a sync layer between two databases — is a permanent source of "why is this out of date" bugs. A shared store has fewer moving parts, and fewer moving parts is the whole reason a small team can operate two products at all.
How permissions actually work
Every member document carries a reference to its coach. Security rules chain that reference: a coach can read a member only if the member points back at them. Privileged actions — reassigning a member, creating a programme template — never run client-side; they go through Cloud Functions that re-check the relationship server-side. The rule of thumb we follow: if getting it wrong leaks data, it does not happen in the browser.
