Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/piratta/gymApp/llms.txt

Use this file to discover all available pages before exploring further.

FocusFlow’s data model is defined entirely in src/types.ts and follows a TypeScript-first design philosophy. Every entity that crosses the React ↔ Firestore boundary is a plain TypeScript interface — no class hierarchies, no ORM abstractions. This keeps serialisation to JSON trivial and ensures that the shape of what is stored in Firestore is always exactly what the TypeScript compiler has verified. The sections below document every major interface, its fields, and how the entities relate to one another.

Core Types

UserRole

type UserRole = "coach" | "client" | "admin";
The role discriminator used throughout the app to gate access to dashboards, filter data queries, and determine which Firestore documents a user can read or write.

User

id
string
required
Unique user identifier. Used as the primary key across all related entities.
role
UserRole
required
Determines which dashboard is rendered: coach, client, or admin.
coachId
string
Present on client users only. References the id of their assigned coach.
isPaused
boolean
When true on a coach, all of that coach’s clients are also auto-logged out.

ClientProfile

clientId
string
required
Foreign key matching User.id. One ClientProfile exists per client user.
routineComplianceRate
number
required
Automatically recalculated on every workout completion as the average completionRate across all of this client’s WorkoutSummary records.
isPlicometriaEnabled
boolean
Coach-controlled flag. When true, the skinfold measurement module is visible in the client’s dashboard.
plicometriaProtocolId
string
Selects which Jackson-Pollock or Yuhasz formula is used when calculating body composition from raw skinfold values.
coachTasks
array
Array of task objects { id, text, completed } that the coach can maintain as a private to-do list for this client.

Exercise

Exercises are stored in the global fit_exercises collection but are filtered by coachId at the dashboard level, so each coach only sees their own custom exercises plus the platform defaults (where coachId is absent).

ExerciseSet

ExerciseSet records appear in two contexts: as the prescribed template inside a RoutineExercise.sets[] and as the athlete’s live performance log inside ActiveWorkoutSession.exercises and WorkoutSummary.loggedExercises.

RoutineExercise


RoutineDay


Routine

isActive
boolean
required
When startDate and endDate are set, isActive is dynamically overridden at render time using useMemo — it is true only when today’s date falls within the date range.

WorkoutSummary


DailyWeight

interface DailyWeight {
  date: string;   // YYYY-MM-DD
  value: number;  // Weight in kg
}
Weight logs are stored as { [clientId: string]: DailyWeight[] } under the fit_weight_logs key. Only one entry per date per client is retained — a new log for the same date overwrites the previous one.

PlicometriaLog

The skinfold fields that are populated depend on the selected protocolId. For example, "3-fold" uses pectoral, abdominal, and musloAnterior for males. All calculated fields (sumaPliegues, densidadCorporal, porcentajeGrasa, masaGrasa, masaMagra) are derived by the client-side formula engine at entry time and stored pre-calculated.

BodyMeasurements

Measurement logs are stored as { [clientId: string]: BodyMeasurements[] } under fit_measurements_logs. When a review is submitted, its measurements object is automatically merged into this log for the matching date.

Review

photos and feedbackPhotos are not persisted to Firestore due to document size constraints. They are stored exclusively in localStorage via the fit_offline_backup_fit_reviews key. The reconciliation logic in loadFromCloud always restores these fields from local backup when merging cloud and local records. Use driveFolderUrl to link to full-resolution files stored in Google Drive.

Message


AgendaEvent


AppNotification

linkTab is used by NotificationCenter to navigate the user to a specific dashboard tab when they tap a notification.

Entity Relationships

User (role: coach)
 └─► ClientProfile (1:1 via clientId)
 └─► Routine[] (1:N via clientId)
      └─► RoutineDay[] (embedded)
           └─► RoutineExercise[] (embedded)
                └─► ExerciseSet[] (embedded)

User (role: client)
 └─► ClientProfile (1:1 via clientId)
 └─► WorkoutSummary[] (1:N via clientId)
 └─► DailyWeight[] (1:N via clientId key in weightLogs map)
 └─► BodyMeasurements[] (1:N via clientId key in measurementsLogs map)
 └─► PlicometriaLog[] (1:N via clientId key in plicometriaLogs map)
 └─► Review[] (1:N via clientId)
 └─► Message[] (1:N as sender or receiver)

User (role: coach)
 └─► AgendaEvent[] (1:N via coachId)
 └─► Exercise[] (1:N via coachId — coach-specific library)
 └─► WorkoutTemplate[] (1:N via coachId)
 └─► CoachProfile (1:1 via coachId)

Firestore Collection Structure

All entities are stored as individual JSON documents inside the single Firestore collection coaching_data. Each document key corresponds to one of the 16 SYNC_KEYS. The document payload is the entire serialised array (or map) for that entity type.
// From src/lib/firebase.ts
export const SYNC_KEYS = [
  "fit_users",                    // User[]
  "fit_client_profiles",          // ClientProfile[]
  "fit_coach_profiles",           // CoachProfile[]
  "fit_exercises",                // Exercise[]
  "fit_templates",                // WorkoutTemplate[]
  "fit_routines",                 // Routine[]
  "fit_weight_logs",              // { [clientId]: DailyWeight[] }
  "fit_measurements_logs",        // { [clientId]: BodyMeasurements[] }
  "fit_plicometria_logs",         // { [clientId]: PlicometriaLog[] }
  "fit_reviews",                  // Review[]
  "fit_messages",                 // Message[]
  "fit_agenda_events",            // AgendaEvent[]
  "fit_summaries",                // WorkoutSummary[]
  "fit_client_calendar_schedule", // { [clientId]: { [dateStr]: dayId } }
  "fit_active_session",           // { [clientId]: ActiveWorkoutSession }
  "fit_notifications",            // AppNotification[]
];
Each Firestore document has the shape:
{
  "payload": "<JSON-serialised array or map>",
  "updatedAt": "2024-11-15T10:32:00.000Z"
}
This single-document-per-collection pattern means all reads and writes for a given entity type operate on the complete array. This simplifies query logic (no where clauses needed) and minimises Firestore read costs, but means large collections (e.g. fit_summaries with many clients) should be monitored for document size as Firestore enforces a 1 MiB per-document limit.

Build docs developers (and LLMs) love