FemeSalud uses TanStack Query (React Query v5) as the single source of truth for all server state. Every data resource exposes query hooks that follow theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/KevxxAlva/femesalud-zen-flow/llms.txt
Use this file to discover all available pages before exploring further.
use{Resource}() naming convention, plus mutation hooks for write operations. Mutation hooks implement full optimistic updates — the UI reflects the change immediately, and the cache is reconciled (or rolled back) once the server responds. Realtime updates from Supabase automatically invalidate the relevant queries, so multiple browser tabs always stay in sync without manual refresh.
onMutate. The cache is updated before the server call completes. If the mutation fails, onError rolls back to the snapshot captured in onMutate. A final onSettled call invalidates the affected query keys to ensure consistency regardless of outcome.Patient Hooks
Exported fromsrc/lib/api/patients.ts. All queries read from the patients Supabase table.
Query Hooks
usePatients()
usePatients()
created_at descending. The list query selects a lightweight projection (id, full_name, email, phone, status, assigned_doctor_id, created_at, updated_at, document_id, historia_number, birth_date) rather than the full row to keep list payloads small.['patients']usePatient(id)
usePatient(id)
*). The query is disabled when id is null or undefined.['patient', id]usePatientCount()
usePatientCount()
number using a count: 'exact' head query — no rows are transferred.['patients_count']usePatientsGrowth()
usePatientsGrowth()
{ created_at: string }[] for all patients created within the last 14 days. Intended for sparkline / growth charts on the dashboard.['patients_growth']usePatientsCountByDateRange(from?, to?)
usePatientsCountByDateRange(from?, to?)
to is automatically padded to T23:59:59.999Z.['patients_count_range', from, to]useRecentPatients(limit?)
useRecentPatients(limit?)
updated_at descending. Defaults to 5 records.['patients_recent', limit]usePaginatedPatients(page, pageSize, search?, status?)
usePaginatedPatients(page, pageSize, search?, status?)
['patients_paginated', page, pageSize, search, status]The search filter uses Supabase’s .or() with ilike across full_name, email, and document_id.Mutation Hooks
useCreatePatient()
useCreatePatient()
document_id and full_name against existing records. Throws a descriptive Spanish-language error if either check fails, so the calling component can surface it directly to the user.['patients_paginated'] and ['patients_recent'] caches.On settled: Invalidates ['patients'], ['patient'], ['patients_count'], ['patients_paginated'], ['patients_recent'].useUpdatePatient()
useUpdatePatient()
id).['patients_paginated'], ['patients_recent'], and the individual ['patient', id] cache entry.On settled: Invalidates the same key families as useCreatePatient, plus ['patient', variables.id].useDeletePatient()
useDeletePatient()
['appointments'] to clear any appointment data that joined the deleted patient.Appointment Hooks
Exported fromsrc/lib/api/appointments.ts. Reads from the appointments table with a join to patients and consultations.
Types
Hooks
useAppointments(filters?)
useAppointments(filters?)
scheduled_at descending. Joins patients(full_name) and consultations(id) to populate patient_name and has_consultation.['appointments'] (no filters) or ['appointments', filters]useCreateAppointment()
useCreateAppointment()
created_by to the authenticated user’s ID automatically.['appointments'].useUpdateAppointment()
useUpdateAppointment()
AppointmentInput fields alongside the required id.['appointments'].useDeleteAppointment()
useDeleteAppointment()
['appointments'].Consultation Hooks
Exported fromsrc/lib/api/consultations.ts. Reads from consultations, with RPCs for create and update to handle the related consultation_consumables rows atomically.
Types
Query Hooks
useConsultations(filters?)
useConsultations(filters?)
id, appointment_id, patient_id, doctor_id, visit_type, is_first_visit, diagnosis, contact_channel, created_at, indications, and the joined patient_name), ordered by created_at descending.['consultations'] or ['consultations', filters]usePatientConsultations(patientId)
usePatientConsultations(patientId)
*) for a single patient, including the related consultation_consumables rows. Disabled when patientId is falsy.['consultations', 'patient', patientId]Each returned item has consumables: ConsultationConsumable[] populated from the join.useConsultationByAppointment(appointmentId)
useConsultationByAppointment(appointmentId)
null if none exists. Uses .maybeSingle() so a missing row is not treated as an error. Disabled when appointmentId is falsy.['consultation', appointmentId]Mutation Hooks
useCreateConsultation()
useCreateConsultation()
create_consultation_rpc Supabase RPC. Consumables are passed in the ConsultationInput.consumables array and handled inside the RPC.['consultations'], ['consultations', 'patient', patientId], and sets ['consultation', appointmentId].On settled: Invalidates ['consultations'], ['consultation', appointmentId], ['consultations', 'patient', patientId], ['appointments'], ['patients'], and ['clinical_notes'].useUpdateConsultation()
useUpdateConsultation()
update_consultation_rpc. Pass the consultation id alongside any partial ConsultationInput fields to patch, plus an optional updated consumables array.['consultations'], ['consultations', 'patient'], and ['consultation', appointmentId].On settled: Invalidates the same key families as useCreateConsultation.Prescription Template Hooks
usePrescriptionTemplates()
usePrescriptionTemplates()
title. Templates are shared across all doctors in the practice.['prescription_templates']useCreatePrescriptionTemplate()
useCreatePrescriptionTemplate()
created_by to the authenticated user’s ID.['prescription_templates'].useDeletePrescriptionTemplate()
useDeletePrescriptionTemplate()
['prescription_templates'].Clinical Notes Hooks
Exported fromsrc/lib/api/clinical-notes.ts. Notes are scoped to a single patient and can carry file attachments stored in the clinical-attachments Supabase Storage bucket.
Types
Hooks
useClinicalNotes(patientId)
useClinicalNotes(patientId)
note_date descending. The attachments field is always an array (never null). The hook is disabled when patientId is falsy.['clinical_notes', patientId]useCreateClinicalNote()
useCreateClinicalNote()
attachments (use uploadClinicalAttachments first). Sets author_id to the authenticated user’s ID.['clinical_notes', patient_id].useDeleteClinicalNote()
useDeleteClinicalNote()
supabase.storage.from('clinical-attachments').remove(paths) before the database row is deleted.On success: Invalidates ['clinical_notes', patient_id].Utility Functions
These are plainasync functions, not hooks — call them directly without useQuery / useMutation.
uploadClinicalAttachments(patientId, files)
uploadClinicalAttachments(patientId, files)
File objects to the clinical-attachments bucket. Each file is stored at a unique path: {patientId}/{timestamp}_{random}_{sanitized_filename}. Returns the metadata array suitable for passing directly to useCreateClinicalNote.getAttachmentUrl(path)
getAttachmentUrl(path)
60 * 30 seconds). For longer-lived links (e.g. WhatsApp sharing), the prescription PDF flow uses a 7-day TTL instead.Profile Hooks
Exported fromsrc/lib/api/profiles.ts. Profiles correspond to authenticated users and store the doctor’s credentials used in PDF documents.
Types
Hooks
useMyProfile(userId)
useMyProfile(userId)
.maybeSingle(), so it returns null rather than throwing when no profile row exists. Disabled when userId is falsy.['profile', userId]useDoctors()
useDoctors()
doctor role by first reading user_roles and then fetching matching profile rows. Returns an empty array when no doctors are found.['doctors']useAllProfilesWithRoles()
useAllProfilesWithRoles()
roles array with zero or more of "admin" and "doctor".['profiles_with_roles']useToggleRole()
useToggleRole()
admin or doctor role for a user. Inserting a duplicate role (enabling when already enabled) is silently ignored.['profiles_with_roles'], ['doctors'], and ['user_roles'].useUpdateProfile()
useUpdateProfile()
null before the update. These fields feed directly into all PDF document signature blocks.['profile', userId], ['profiles_with_roles'], and ['doctors'].Clinic Info Hooks
Exported fromsrc/lib/api/clinic.ts. The clinic info table contains exactly one row (id = 1) that holds the address and contact details printed on every PDF document.
Types
Hooks
useClinicInfo()
useClinicInfo()
.single(). Throws if no row is found.['clinic_info']useUpdateClinicInfo()
useUpdateClinicInfo()
ClinicInfo fields (except id and updated_at). updated_at is automatically set to the current timestamp.['clinic_info'].Auth Hooks
Exported fromsrc/hooks/useAuth.ts. These hooks manage Supabase authentication state and role-based access control.
Types
Hooks
useAuthSession()
useAuthSession()
onAuthStateChange and returns the current auth state. Returns loading: true on the initial render until the session is confirmed.useState and useEffect internally to manage the Supabase auth subscription. The subscription is cleaned up automatically on unmount.useRoles()
useRoles()
user_roles rows for the currently authenticated user. Returns an array of AppRole values. The query is disabled when no user is logged in.['user_roles', user.id]useIsAdmin()
useIsAdmin()
useRoles(). Returns true when the current user’s roles include "admin".Theme Hook
Exported fromsrc/hooks/useTheme.ts. Manages the light / dark theme preference using localStorage and the system prefers-color-scheme media query as a default.
useTheme()
useTheme()
localStorage on first render, falling back to the OS preference. Applies the "dark" class to document.documentElement reactively when the theme is "dark", and removes it when "light".theme — current theme value.toggleTheme() — switches between "light" and "dark" and persists the choice to localStorage.isDark — shorthand boolean for conditional rendering and icon toggling.Realtime Sync
Exported fromsrc/hooks/useRealtimeSync.ts. This hook is called once at the root of the authenticated layout, not in individual page components.
useRealtimeSync()
useRealtimeSync()
postgres_changes events (INSERT, UPDATE, DELETE) on the core tables and automatically invalidate the corresponding React Query caches.| Channel | Table | Invalidated query keys |
|---|---|---|
patients-realtime | patients | ['patients'], ['patient'], ['patients_count'], ['patients_paginated'], ['patients_recent'] |
appointments-realtime | appointments | ['appointments'], ['appointments_growth'], ['patients_count'], ['patients_count_range'] |
consultations-realtime | consultations | ['consultations'], ['consultation'], ['appointments'] |
clinical-notes-realtime | clinical_notes | ['clinical_notes'] |
templates-realtime | prescription_templates | ['prescription_templates'] |
supabase.removeChannel).