feat: CRM Clinicas SaaS - MVP completo

- Auth: Login/Register con creacion de clinica
- Dashboard: KPIs reales, graficas recharts
- Pacientes: CRUD completo con busqueda
- Agenda: FullCalendar, drag-and-drop, vista recepcion
- Expediente: Notas SOAP, signos vitales, CIE-10
- Facturacion: Facturas con IVA, campos CFDI SAT
- Inventario: Productos, stock, movimientos, alertas
- Configuracion: Clinica, equipo, catalogo servicios
- Supabase self-hosted: 18 tablas con RLS multi-tenant
- Docker + Nginx para produccion

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
Consultoria AS
2026-03-03 07:04:14 +00:00
commit 79b5d86325
1612 changed files with 109181 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE TABLE clinics (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
logo_url TEXT,
address TEXT,
phone TEXT,
email TEXT,
rfc TEXT,
razon_social TEXT,
regimen_fiscal TEXT,
codigo_postal TEXT,
pac_provider TEXT CHECK (pac_provider IN ('facturama', 'sw_sapien')),
pac_api_key TEXT,
subscription_plan TEXT NOT NULL DEFAULT 'trial' CHECK (subscription_plan IN ('basico', 'pro', 'enterprise', 'trial')),
subscription_status TEXT NOT NULL DEFAULT 'trial' CHECK (subscription_status IN ('active', 'trial', 'suspended', 'cancelled')),
trial_ends_at TIMESTAMPTZ DEFAULT (NOW() + INTERVAL '14 days'),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE users (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
full_name TEXT NOT NULL,
email TEXT NOT NULL,
phone TEXT,
role TEXT NOT NULL DEFAULT 'receptionist' CHECK (role IN ('owner', 'admin', 'doctor', 'receptionist')),
specialty TEXT,
license_number TEXT,
color TEXT DEFAULT '#3B82F6',
is_active BOOLEAN NOT NULL DEFAULT TRUE,
avatar_url TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_users_clinic ON users(clinic_id);
CREATE TABLE branches (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
name TEXT NOT NULL,
address TEXT,
phone TEXT,
is_main BOOLEAN NOT NULL DEFAULT FALSE,
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
CREATE INDEX idx_branches_clinic ON branches(clinic_id);
CREATE OR REPLACE FUNCTION auth.clinic_id()
RETURNS UUID AS $$
SELECT clinic_id FROM public.users WHERE id = auth.uid()
$$ LANGUAGE SQL STABLE SECURITY DEFINER;
ALTER TABLE clinics ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view own clinic" ON clinics FOR SELECT USING (id = auth.clinic_id());
CREATE POLICY "Owners can update own clinic" ON clinics FOR UPDATE USING (id = auth.clinic_id());
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view clinic members" ON users FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Owners can insert users" ON users FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());
CREATE POLICY "Owners can update users" ON users FOR UPDATE USING (clinic_id = auth.clinic_id());
ALTER TABLE branches ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view clinic branches" ON branches FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Users can manage branches" ON branches FOR ALL USING (clinic_id = auth.clinic_id());

View File

@@ -0,0 +1,28 @@
CREATE TABLE patients (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
date_of_birth DATE,
gender TEXT CHECK (gender IN ('M', 'F', 'otro')),
curp TEXT,
phone TEXT,
email TEXT,
address JSONB,
blood_type TEXT,
allergies TEXT[] DEFAULT '{}',
emergency_contact JSONB,
notes TEXT,
source TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_patients_clinic ON patients(clinic_id);
CREATE INDEX idx_patients_name ON patients(clinic_id, last_name, first_name);
CREATE INDEX idx_patients_phone ON patients(clinic_id, phone);
ALTER TABLE patients ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can view clinic patients" ON patients FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Users can insert clinic patients" ON patients FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());
CREATE POLICY "Users can update clinic patients" ON patients FOR UPDATE USING (clinic_id = auth.clinic_id());

View File

@@ -0,0 +1,40 @@
CREATE TABLE doctor_schedules (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
doctor_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
day_of_week INT NOT NULL CHECK (day_of_week BETWEEN 0 AND 6),
start_time TIME NOT NULL,
end_time TIME NOT NULL,
slot_duration INT NOT NULL DEFAULT 30,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
UNIQUE(doctor_id, day_of_week)
);
CREATE TABLE appointments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
patient_id UUID NOT NULL REFERENCES patients(id) ON DELETE CASCADE,
doctor_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
starts_at TIMESTAMPTZ NOT NULL,
ends_at TIMESTAMPTZ NOT NULL,
status TEXT NOT NULL DEFAULT 'scheduled' CHECK (status IN ('scheduled','confirmed','in_progress','completed','cancelled','no_show')),
type TEXT NOT NULL DEFAULT 'seguimiento' CHECK (type IN ('primera_vez','seguimiento','urgencia')),
reason TEXT,
notes TEXT,
reminder_sent BOOLEAN NOT NULL DEFAULT FALSE,
created_by UUID REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_appointments_doctor_date ON appointments(doctor_id, starts_at);
CREATE INDEX idx_appointments_patient ON appointments(patient_id);
CREATE INDEX idx_appointments_status ON appointments(clinic_id, status);
ALTER TABLE doctor_schedules ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic schedules" ON doctor_schedules FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Manage clinic schedules" ON doctor_schedules FOR ALL USING (clinic_id = auth.clinic_id());
ALTER TABLE appointments ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic appointments" ON appointments FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Insert clinic appointments" ON appointments FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());
CREATE POLICY "Update clinic appointments" ON appointments FOR UPDATE USING (clinic_id = auth.clinic_id());

View File

@@ -0,0 +1,75 @@
CREATE TABLE medical_records (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
patient_id UUID NOT NULL REFERENCES patients(id) ON DELETE CASCADE,
family_history JSONB DEFAULT '[]',
personal_history JSONB DEFAULT '[]',
surgical_history JSONB DEFAULT '[]',
current_medications JSONB DEFAULT '[]',
immunizations JSONB DEFAULT '[]',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(patient_id)
);
CREATE TABLE consultation_notes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
patient_id UUID NOT NULL REFERENCES patients(id) ON DELETE CASCADE,
doctor_id UUID NOT NULL REFERENCES users(id),
appointment_id UUID REFERENCES appointments(id),
subjective TEXT,
objective TEXT,
assessment TEXT,
plan TEXT,
vital_signs JSONB,
diagnoses JSONB DEFAULT '[]',
is_signed BOOLEAN NOT NULL DEFAULT FALSE,
signed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_consultations_patient ON consultation_notes(patient_id, created_at DESC);
CREATE INDEX idx_consultations_doctor ON consultation_notes(doctor_id, created_at DESC);
CREATE TABLE prescriptions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
consultation_id UUID NOT NULL REFERENCES consultation_notes(id) ON DELETE CASCADE,
patient_id UUID NOT NULL REFERENCES patients(id),
doctor_id UUID NOT NULL REFERENCES users(id),
items JSONB NOT NULL DEFAULT '[]',
pharmacy_notes TEXT,
pdf_url TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE medical_files (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
patient_id UUID NOT NULL REFERENCES patients(id) ON DELETE CASCADE,
consultation_id UUID REFERENCES consultation_notes(id),
file_name TEXT NOT NULL,
file_type TEXT NOT NULL DEFAULT 'other' CHECK (file_type IN ('lab_result','imaging','referral','other')),
storage_path TEXT NOT NULL,
uploaded_by UUID REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ALTER TABLE medical_records ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic records" ON medical_records FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Insert clinic records" ON medical_records FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());
CREATE POLICY "Update clinic records" ON medical_records FOR UPDATE USING (clinic_id = auth.clinic_id());
ALTER TABLE consultation_notes ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic notes" ON consultation_notes FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Insert clinic notes" ON consultation_notes FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());
CREATE POLICY "Update own notes" ON consultation_notes FOR UPDATE USING (clinic_id = auth.clinic_id() AND doctor_id = auth.uid());
ALTER TABLE prescriptions ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic prescriptions" ON prescriptions FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Insert clinic prescriptions" ON prescriptions FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());
ALTER TABLE medical_files ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic files" ON medical_files FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Insert clinic files" ON medical_files FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());

View File

@@ -0,0 +1,78 @@
CREATE TABLE services_catalog (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
name TEXT NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL DEFAULT 0,
sat_product_key TEXT,
sat_unit_key TEXT DEFAULT 'E48',
tax_rate DECIMAL(4,2) NOT NULL DEFAULT 0.16,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE invoices (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
patient_id UUID NOT NULL REFERENCES patients(id),
invoice_number TEXT NOT NULL,
cfdi_uuid TEXT,
cfdi_status TEXT NOT NULL DEFAULT 'draft' CHECK (cfdi_status IN ('draft','stamped','cancelled')),
cfdi_xml_url TEXT,
cfdi_pdf_url TEXT,
uso_cfdi TEXT DEFAULT 'S01',
forma_pago TEXT DEFAULT '01',
metodo_pago TEXT DEFAULT 'PUE',
subtotal DECIMAL(10,2) NOT NULL DEFAULT 0,
tax_amount DECIMAL(10,2) NOT NULL DEFAULT 0,
total DECIMAL(10,2) NOT NULL DEFAULT 0,
payment_status TEXT NOT NULL DEFAULT 'pending' CHECK (payment_status IN ('pending','partial','paid')),
paid_amount DECIMAL(10,2) NOT NULL DEFAULT 0,
due_date DATE,
created_by UUID REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(clinic_id, invoice_number)
);
CREATE INDEX idx_invoices_patient ON invoices(patient_id);
CREATE INDEX idx_invoices_status ON invoices(clinic_id, payment_status);
CREATE TABLE invoice_items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
invoice_id UUID NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
service_id UUID REFERENCES services_catalog(id),
product_id UUID,
description TEXT NOT NULL,
quantity DECIMAL(10,2) NOT NULL DEFAULT 1,
unit_price DECIMAL(10,2) NOT NULL DEFAULT 0,
tax_rate DECIMAL(4,2) NOT NULL DEFAULT 0.16,
total DECIMAL(10,2) NOT NULL DEFAULT 0
);
CREATE TABLE payments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
invoice_id UUID NOT NULL REFERENCES invoices(id) ON DELETE CASCADE,
amount DECIMAL(10,2) NOT NULL,
payment_method TEXT NOT NULL DEFAULT 'cash' CHECK (payment_method IN ('cash','card','transfer','other')),
reference TEXT,
received_by UUID REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ALTER TABLE services_catalog ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic services" ON services_catalog FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Manage clinic services" ON services_catalog FOR ALL USING (clinic_id = auth.clinic_id());
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic invoices" ON invoices FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Insert clinic invoices" ON invoices FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());
CREATE POLICY "Update clinic invoices" ON invoices FOR UPDATE USING (clinic_id = auth.clinic_id());
ALTER TABLE invoice_items ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View invoice items" ON invoice_items FOR SELECT USING (EXISTS (SELECT 1 FROM invoices WHERE invoices.id = invoice_items.invoice_id AND invoices.clinic_id = auth.clinic_id()));
CREATE POLICY "Manage invoice items" ON invoice_items FOR ALL USING (EXISTS (SELECT 1 FROM invoices WHERE invoices.id = invoice_items.invoice_id AND invoices.clinic_id = auth.clinic_id()));
ALTER TABLE payments ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic payments" ON payments FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Insert clinic payments" ON payments FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());

View File

@@ -0,0 +1,64 @@
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
sku TEXT,
name TEXT NOT NULL,
description TEXT,
category TEXT NOT NULL DEFAULT 'insumo' CHECK (category IN ('medicamento','insumo','material','equipo')),
unit TEXT NOT NULL DEFAULT 'pieza',
purchase_price DECIMAL(10,2) NOT NULL DEFAULT 0,
sale_price DECIMAL(10,2) NOT NULL DEFAULT 0,
min_stock INT NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ALTER TABLE invoice_items ADD CONSTRAINT fk_invoice_items_product FOREIGN KEY (product_id) REFERENCES products(id);
CREATE TABLE inventory_stock (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
current_stock DECIMAL(10,2) NOT NULL DEFAULT 0,
last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(clinic_id, product_id)
);
CREATE TABLE inventory_movements (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID NOT NULL REFERENCES clinics(id) ON DELETE CASCADE,
product_id UUID NOT NULL REFERENCES products(id) ON DELETE CASCADE,
type TEXT NOT NULL CHECK (type IN ('entrada','salida','ajuste','merma')),
quantity DECIMAL(10,2) NOT NULL,
reason TEXT,
reference_id TEXT,
created_by UUID REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_movements_product ON inventory_movements(product_id, created_at DESC);
CREATE OR REPLACE FUNCTION update_inventory_stock()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO inventory_stock (clinic_id, product_id, current_stock, last_updated)
VALUES (NEW.clinic_id, NEW.product_id, NEW.quantity, NOW())
ON CONFLICT (clinic_id, product_id)
DO UPDATE SET current_stock = inventory_stock.current_stock + NEW.quantity, last_updated = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER trg_update_stock AFTER INSERT ON inventory_movements FOR EACH ROW EXECUTE FUNCTION update_inventory_stock();
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic products" ON products FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Manage clinic products" ON products FOR ALL USING (clinic_id = auth.clinic_id());
ALTER TABLE inventory_stock ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic stock" ON inventory_stock FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Manage clinic stock" ON inventory_stock FOR ALL USING (clinic_id = auth.clinic_id());
ALTER TABLE inventory_movements ENABLE ROW LEVEL SECURITY;
CREATE POLICY "View clinic movements" ON inventory_movements FOR SELECT USING (clinic_id = auth.clinic_id());
CREATE POLICY "Insert clinic movements" ON inventory_movements FOR INSERT WITH CHECK (clinic_id = auth.clinic_id());

View File

@@ -0,0 +1,41 @@
CREATE TABLE audit_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
clinic_id UUID REFERENCES clinics(id),
user_id UUID,
table_name TEXT NOT NULL,
record_id UUID,
action TEXT NOT NULL CHECK (action IN ('INSERT','UPDATE','DELETE')),
old_data JSONB,
new_data JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_audit_clinic ON audit_log(clinic_id, created_at DESC);
CREATE OR REPLACE FUNCTION audit_trigger_func()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO audit_log (clinic_id, user_id, table_name, record_id, action, new_data)
VALUES (NEW.clinic_id, auth.uid(), TG_TABLE_NAME, NEW.id, 'INSERT', to_jsonb(NEW));
RETURN NEW;
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO audit_log (clinic_id, user_id, table_name, record_id, action, old_data, new_data)
VALUES (NEW.clinic_id, auth.uid(), TG_TABLE_NAME, NEW.id, 'UPDATE', to_jsonb(OLD), to_jsonb(NEW));
RETURN NEW;
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO audit_log (clinic_id, user_id, table_name, record_id, action, old_data)
VALUES (OLD.clinic_id, auth.uid(), TG_TABLE_NAME, OLD.id, 'DELETE', to_jsonb(OLD));
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER audit_patients AFTER INSERT OR UPDATE OR DELETE ON patients FOR EACH ROW EXECUTE FUNCTION audit_trigger_func();
CREATE TRIGGER audit_appointments AFTER INSERT OR UPDATE OR DELETE ON appointments FOR EACH ROW EXECUTE FUNCTION audit_trigger_func();
CREATE TRIGGER audit_consultation_notes AFTER INSERT OR UPDATE OR DELETE ON consultation_notes FOR EACH ROW EXECUTE FUNCTION audit_trigger_func();
CREATE TRIGGER audit_invoices AFTER INSERT OR UPDATE OR DELETE ON invoices FOR EACH ROW EXECUTE FUNCTION audit_trigger_func();
CREATE TRIGGER audit_inventory_movements AFTER INSERT ON inventory_movements FOR EACH ROW EXECUTE FUNCTION audit_trigger_func();
ALTER TABLE audit_log ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Admins view audit" ON audit_log FOR SELECT USING (clinic_id = auth.clinic_id());

View File

@@ -0,0 +1,22 @@
-- Fix: Allow new users to create clinics and profiles during registration
-- The original RLS policies required a clinic_id which doesn't exist yet at signup time
-- Allow any authenticated user to create a clinic (for registration flow)
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies WHERE policyname = 'Authenticated users can create clinics' AND tablename = 'clinics'
) THEN
CREATE POLICY "Authenticated users can create clinics" ON clinics
FOR INSERT WITH CHECK (auth.uid() IS NOT NULL);
END IF;
END $$;
-- Allow users to insert their own profile row
DO $$ BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_policies WHERE policyname = 'Users can insert own profile' AND tablename = 'users'
) THEN
CREATE POLICY "Users can insert own profile" ON users
FOR INSERT WITH CHECK (id = auth.uid());
END IF;
END $$;