diff --git a/backend/drizzle/0025_nice_paladin.sql b/backend/drizzle/0025_nice_paladin.sql new file mode 100644 index 0000000..8602e97 --- /dev/null +++ b/backend/drizzle/0025_nice_paladin.sql @@ -0,0 +1,14 @@ +CREATE TABLE "scheduled_meetings" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "organization_id" text NOT NULL, + "title" text NOT NULL, + "date" text NOT NULL, + "time" text NOT NULL, + "participants" jsonb DEFAULT '[]'::jsonb NOT NULL, + "created_by" text, + "created_at" timestamp DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "scheduled_meetings" ADD CONSTRAINT "scheduled_meetings_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "scheduled_meetings" ADD CONSTRAINT "scheduled_meetings_created_by_user_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."user"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "scheduled_meetings_org_idx" ON "scheduled_meetings" USING btree ("organization_id"); \ No newline at end of file diff --git a/backend/drizzle/meta/0025_snapshot.json b/backend/drizzle/meta/0025_snapshot.json new file mode 100644 index 0000000..4940043 --- /dev/null +++ b/backend/drizzle/meta/0025_snapshot.json @@ -0,0 +1,3998 @@ +{ + "id": "0e7675a7-57c2-4f8d-b584-693d53200c53", + "prevId": "5b02186d-d528-4a05-8631-7146bcf48ca4", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "invitation_organizationId_idx": { + "name": "invitation_organizationId_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invitation_email_idx": { + "name": "invitation_email_idx", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "member_organizationId_idx": { + "name": "member_organizationId_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "member_userId_idx": { + "name": "member_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "organization_slug_uidx": { + "name": "organization_slug_uidx", + "columns": [ + { + "expression": "slug", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.rate_limit": { + "name": "rate_limit", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "last_request": { + "name": "last_request", + "type": "bigint", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "rate_limit_key_unique": { + "name": "rate_limit_key_unique", + "nullsNotDistinct": false, + "columns": [ + "key" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "display_username": { + "name": "display_username", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + }, + "user_username_unique": { + "name": "user_username_unique", + "nullsNotDistinct": false, + "columns": [ + "username" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.patient_allergies": { + "name": "patient_allergies", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "patient_id": { + "name": "patient_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "substance": { + "name": "substance", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reaction": { + "name": "reaction", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "severity": { + "name": "severity", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "allergies_patient_idx": { + "name": "allergies_patient_idx", + "columns": [ + { + "expression": "patient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "patient_allergies_patient_id_patients_id_fk": { + "name": "patient_allergies_patient_id_patients_id_fk", + "tableFrom": "patient_allergies", + "tableTo": "patients", + "columnsFrom": [ + "patient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.patient_encounters": { + "name": "patient_encounters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "patient_id": { + "name": "patient_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "encounters_patient_idx": { + "name": "encounters_patient_idx", + "columns": [ + { + "expression": "patient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "patient_encounters_patient_id_patients_id_fk": { + "name": "patient_encounters_patient_id_patients_id_fk", + "tableFrom": "patient_encounters", + "tableTo": "patients", + "columnsFrom": [ + "patient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.patient_labs": { + "name": "patient_labs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "patient_id": { + "name": "patient_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "flag": { + "name": "flag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "taken_at": { + "name": "taken_at", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "labs_patient_idx": { + "name": "labs_patient_idx", + "columns": [ + { + "expression": "patient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "patient_labs_patient_id_patients_id_fk": { + "name": "patient_labs_patient_id_patients_id_fk", + "tableFrom": "patient_labs", + "tableTo": "patients", + "columnsFrom": [ + "patient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.patient_medications": { + "name": "patient_medications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "patient_id": { + "name": "patient_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dose": { + "name": "dose", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "frequency": { + "name": "frequency", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "medications_patient_idx": { + "name": "medications_patient_idx", + "columns": [ + { + "expression": "patient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "patient_medications_patient_id_patients_id_fk": { + "name": "patient_medications_patient_id_patients_id_fk", + "tableFrom": "patient_medications", + "tableTo": "patients", + "columnsFrom": [ + "patient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.patients": { + "name": "patients", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_number": { + "name": "file_number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "age": { + "name": "age", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "sex": { + "name": "sex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pcp": { + "name": "pcp", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "initials": { + "name": "initials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alerts": { + "name": "alerts", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "vitals_bp": { + "name": "vitals_bp", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vitals_hr": { + "name": "vitals_hr", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vitals_temp": { + "name": "vitals_temp", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vitals_spo2": { + "name": "vitals_spo2", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vitals_taken_at": { + "name": "vitals_taken_at", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "vitals_trend": { + "name": "vitals_trend", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "lab_trend": { + "name": "lab_trend", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "primary_provider_id": { + "name": "primary_provider_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "patients_org_file_uidx": { + "name": "patients_org_file_uidx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "patients_org_idx": { + "name": "patients_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "patients_organization_id_organization_id_fk": { + "name": "patients_organization_id_organization_id_fk", + "tableFrom": "patients", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "patients_primary_provider_id_user_id_fk": { + "name": "patients_primary_provider_id_user_id_fk", + "tableFrom": "patients", + "tableTo": "user", + "columnsFrom": [ + "primary_provider_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "patients_created_by_user_id_fk": { + "name": "patients_created_by_user_id_fk", + "tableFrom": "patients", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.patient_problems": { + "name": "patient_problems", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "patient_id": { + "name": "patient_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "label": { + "name": "label", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "since": { + "name": "since", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "problems_patient_idx": { + "name": "problems_patient_idx", + "columns": [ + { + "expression": "patient_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "patient_problems_patient_id_patients_id_fk": { + "name": "patient_problems_patient_id_patients_id_fk", + "tableFrom": "patient_problems", + "tableTo": "patients", + "columnsFrom": [ + "patient_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notes": { + "name": "notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "author_id": { + "name": "author_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notes_org_author_idx": { + "name": "notes_org_author_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "author_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notes_organization_id_organization_id_fk": { + "name": "notes_organization_id_organization_id_fk", + "tableFrom": "notes", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notes_author_id_user_id_fk": { + "name": "notes_author_id_user_id_fk", + "tableFrom": "notes", + "tableTo": "user", + "columnsFrom": [ + "author_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.appointments": { + "name": "appointments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "patient_file_number": { + "name": "patient_file_number", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "patient_name": { + "name": "patient_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "patient_initials": { + "name": "patient_initials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "time": { + "name": "time", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "appointments_org_date_idx": { + "name": "appointments_org_date_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "date", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "appointments_organization_id_organization_id_fk": { + "name": "appointments_organization_id_organization_id_fk", + "tableFrom": "appointments", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "appointments_created_by_user_id_fk": { + "name": "appointments_created_by_user_id_fk", + "tableFrom": "appointments", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.prescriptions": { + "name": "prescriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "patient_file_number": { + "name": "patient_file_number", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "patient_name": { + "name": "patient_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "patient_initials": { + "name": "patient_initials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "medication": { + "name": "medication", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dose": { + "name": "dose", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "frequency": { + "name": "frequency", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prescriber": { + "name": "prescriber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prescribed_at": { + "name": "prescribed_at", + "type": "date", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "start_date": { + "name": "start_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "duration": { + "name": "duration", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "prescriptions_org_idx": { + "name": "prescriptions_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "prescriptions_organization_id_organization_id_fk": { + "name": "prescriptions_organization_id_organization_id_fk", + "tableFrom": "prescriptions", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "prescriptions_created_by_user_id_fk": { + "name": "prescriptions_created_by_user_id_fk", + "tableFrom": "prescriptions", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invoices": { + "name": "invoices", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "patient_file_number": { + "name": "patient_file_number", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "patient_name": { + "name": "patient_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "patient_initials": { + "name": "patient_initials", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "number": { + "name": "number", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issued_at": { + "name": "issued_at", + "type": "date", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "due_at": { + "name": "due_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "line_items": { + "name": "line_items", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "installments": { + "name": "installments", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'manual'" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "invoices_org_idx": { + "name": "invoices_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "invoices_org_file_idx": { + "name": "invoices_org_file_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "patient_file_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "invoices_organization_id_organization_id_fk": { + "name": "invoices_organization_id_organization_id_fk", + "tableFrom": "invoices", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invoices_created_by_user_id_fk": { + "name": "invoices_created_by_user_id_fk", + "tableFrom": "invoices", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inventory": { + "name": "inventory", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "form": { + "name": "form", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "strength": { + "name": "strength", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "stock_quantity": { + "name": "stock_quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reorder_threshold": { + "name": "reorder_threshold", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "location": { + "name": "location", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "expires_at": { + "name": "expires_at", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inventory_org_idx": { + "name": "inventory_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inventory_organization_id_organization_id_fk": { + "name": "inventory_organization_id_organization_id_fk", + "tableFrom": "inventory", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inventory_created_by_user_id_fk": { + "name": "inventory_created_by_user_id_fk", + "tableFrom": "inventory", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dispenses": { + "name": "dispenses", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "patient_file_number": { + "name": "patient_file_number", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "patient_name": { + "name": "patient_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "patient_initials": { + "name": "patient_initials", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "medication": { + "name": "medication", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dose": { + "name": "dose", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "prescription_id": { + "name": "prescription_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dispensed_by": { + "name": "dispensed_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dispensed_by_name": { + "name": "dispensed_by_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "dispensed_at": { + "name": "dispensed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "dispenses_org_idx": { + "name": "dispenses_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "dispenses_organization_id_organization_id_fk": { + "name": "dispenses_organization_id_organization_id_fk", + "tableFrom": "dispenses", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "dispenses_dispensed_by_user_id_fk": { + "name": "dispenses_dispensed_by_user_id_fk", + "tableFrom": "dispenses", + "tableTo": "user", + "columnsFrom": [ + "dispensed_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.tasks": { + "name": "tasks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "assignee": { + "name": "assignee", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'Unassigned'" + }, + "assignee_role": { + "name": "assignee_role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "due": { + "name": "due", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'No due date'" + }, + "priority": { + "name": "priority", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'todo'" + }, + "patient": { + "name": "patient", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "done": { + "name": "done", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_by_name": { + "name": "created_by_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "tasks_org_idx": { + "name": "tasks_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "tasks_organization_id_organization_id_fk": { + "name": "tasks_organization_id_organization_id_fk", + "tableFrom": "tasks", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "tasks_created_by_user_id_fk": { + "name": "tasks_created_by_user_id_fk", + "tableFrom": "tasks", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.activity_log": { + "name": "activity_log", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "patient_name": { + "name": "patient_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "patient_file_number": { + "name": "patient_file_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "activity_org_created_idx": { + "name": "activity_org_created_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "activity_log_organization_id_organization_id_fk": { + "name": "activity_log_organization_id_organization_id_fk", + "tableFrom": "activity_log", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "activity_log_actor_id_user_id_fk": { + "name": "activity_log_actor_id_user_id_fk", + "tableFrom": "activity_log", + "tableTo": "user", + "columnsFrom": [ + "actor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.conversation_participants": { + "name": "conversation_participants", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_read_at": { + "name": "last_read_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "conv_participant_uidx": { + "name": "conv_participant_uidx", + "columns": [ + { + "expression": "conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "conv_participant_user_idx": { + "name": "conv_participant_user_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "conversation_participants_conversation_id_conversations_id_fk": { + "name": "conversation_participants_conversation_id_conversations_id_fk", + "tableFrom": "conversation_participants", + "tableTo": "conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "conversation_participants_user_id_user_id_fk": { + "name": "conversation_participants_user_id_user_id_fk", + "tableFrom": "conversation_participants", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.conversations": { + "name": "conversations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_group": { + "name": "is_group", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "conversations_org_idx": { + "name": "conversations_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "conversations_organization_id_organization_id_fk": { + "name": "conversations_organization_id_organization_id_fk", + "tableFrom": "conversations", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "conversations_created_by_user_id_fk": { + "name": "conversations_created_by_user_id_fk", + "tableFrom": "conversations", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.message_attachments": { + "name": "message_attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploader_id": { + "name": "uploader_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "file_name": { + "name": "file_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "message_attachments_org_idx": { + "name": "message_attachments_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "message_attachments_organization_id_organization_id_fk": { + "name": "message_attachments_organization_id_organization_id_fk", + "tableFrom": "message_attachments", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "message_attachments_uploader_id_user_id_fk": { + "name": "message_attachments_uploader_id_user_id_fk", + "tableFrom": "message_attachments", + "tableTo": "user", + "columnsFrom": [ + "uploader_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.messages": { + "name": "messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "conversation_id": { + "name": "conversation_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "sender_id": { + "name": "sender_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "attachments": { + "name": "attachments", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "messages_conv_idx": { + "name": "messages_conv_idx", + "columns": [ + { + "expression": "conversation_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "messages_conversation_id_conversations_id_fk": { + "name": "messages_conversation_id_conversations_id_fk", + "tableFrom": "messages", + "tableTo": "conversations", + "columnsFrom": [ + "conversation_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "messages_sender_id_user_id_fk": { + "name": "messages_sender_id_user_id_fk", + "tableFrom": "messages", + "tableTo": "user", + "columnsFrom": [ + "sender_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notifications": { + "name": "notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "read": { + "name": "read", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_name": { + "name": "actor_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "actor_initials": { + "name": "actor_initials", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "notifications_org_user_read_idx": { + "name": "notifications_org_user_read_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "read", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "notifications_organization_id_organization_id_fk": { + "name": "notifications_organization_id_organization_id_fk", + "tableFrom": "notifications", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notifications_user_id_user_id_fk": { + "name": "notifications_user_id_user_id_fk", + "tableFrom": "notifications", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_settings": { + "name": "user_settings", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "preferences": { + "name": "preferences", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_settings_user_id_user_id_fk": { + "name": "user_settings_user_id_user_id_fk", + "tableFrom": "user_settings", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_ai_settings": { + "name": "user_ai_settings", + "schema": "", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "mode": { + "name": "mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'local'" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'anthropic'" + }, + "ollama_base_url": { + "name": "ollama_base_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'http://localhost:11434'" + }, + "ollama_model": { + "name": "ollama_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'llama3.1'" + }, + "default_model": { + "name": "default_model", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'claude-sonnet-4-6'" + }, + "default_effort": { + "name": "default_effort", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'medium'" + }, + "veil_level": { + "name": "veil_level", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'full'" + }, + "api_keys_cipher": { + "name": "api_keys_cipher", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_ai_settings_user_id_user_id_fk": { + "name": "user_ai_settings_user_id_user_id_fk", + "tableFrom": "user_ai_settings", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_chat_messages": { + "name": "ai_chat_messages", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "thread_id": { + "name": "thread_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parts": { + "name": "parts", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_messages_thread_idx": { + "name": "ai_messages_thread_idx", + "columns": [ + { + "expression": "thread_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "position", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_chat_messages_thread_id_ai_chat_threads_id_fk": { + "name": "ai_chat_messages_thread_id_ai_chat_threads_id_fk", + "tableFrom": "ai_chat_messages", + "tableTo": "ai_chat_threads", + "columnsFrom": [ + "thread_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ai_chat_threads": { + "name": "ai_chat_threads", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'New chat'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "ai_threads_org_user_idx": { + "name": "ai_threads_org_user_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "ai_chat_threads_organization_id_organization_id_fk": { + "name": "ai_chat_threads_organization_id_organization_id_fk", + "tableFrom": "ai_chat_threads", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "ai_chat_threads_user_id_user_id_fk": { + "name": "ai_chat_threads_user_id_user_id_fk", + "tableFrom": "ai_chat_threads", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.org_ai_policy": { + "name": "org_ai_policy", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "ai_enabled": { + "name": "ai_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "disabled_for_employees": { + "name": "disabled_for_employees", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "org_ai_policy_organization_id_organization_id_fk": { + "name": "org_ai_policy_organization_id_organization_id_fk", + "tableFrom": "org_ai_policy", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.attachments": { + "name": "attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "file_number": { + "name": "file_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "lab_key": { + "name": "lab_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size_bytes": { + "name": "size_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "storage_path": { + "name": "storage_path", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uploaded_by_user_id": { + "name": "uploaded_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "attachments_org_file_idx": { + "name": "attachments_org_file_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "file_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "attachments_organization_id_organization_id_fk": { + "name": "attachments_organization_id_organization_id_fk", + "tableFrom": "attachments", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "attachments_uploaded_by_user_id_user_id_fk": { + "name": "attachments_uploaded_by_user_id_user_id_fk", + "tableFrom": "attachments", + "tableTo": "user", + "columnsFrom": [ + "uploaded_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.integrations": { + "name": "integrations", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'unconfigured'" + }, + "last_sync_at": { + "name": "last_sync_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "integrations_organization_id_organization_id_fk": { + "name": "integrations_organization_id_organization_id_fk", + "tableFrom": "integrations", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "integrations_organization_id_type_pk": { + "name": "integrations_organization_id_type_pk", + "columns": [ + "organization_id", + "type" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.staff_profile": { + "name": "staff_profile", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "specialty": { + "name": "specialty", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "staff_profile_org_user_idx": { + "name": "staff_profile_org_user_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "staff_profile_organization_id_organization_id_fk": { + "name": "staff_profile_organization_id_organization_id_fk", + "tableFrom": "staff_profile", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "staff_profile_user_id_user_id_fk": { + "name": "staff_profile_user_id_user_id_fk", + "tableFrom": "staff_profile", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.meeting_rooms": { + "name": "meeting_rooms", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "meeting_rooms_org_idx": { + "name": "meeting_rooms_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "meeting_rooms_organization_id_organization_id_fk": { + "name": "meeting_rooms_organization_id_organization_id_fk", + "tableFrom": "meeting_rooms", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "meeting_rooms_created_by_user_id_fk": { + "name": "meeting_rooms_created_by_user_id_fk", + "tableFrom": "meeting_rooms", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.scheduled_meetings": { + "name": "scheduled_meetings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "date": { + "name": "date", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "time": { + "name": "time", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "participants": { + "name": "participants", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "scheduled_meetings_org_idx": { + "name": "scheduled_meetings_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "scheduled_meetings_organization_id_organization_id_fk": { + "name": "scheduled_meetings_organization_id_organization_id_fk", + "tableFrom": "scheduled_meetings", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "scheduled_meetings_created_by_user_id_fk": { + "name": "scheduled_meetings_created_by_user_id_fk", + "tableFrom": "scheduled_meetings", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/backend/drizzle/meta/_journal.json b/backend/drizzle/meta/_journal.json index 3920842..b690d23 100644 --- a/backend/drizzle/meta/_journal.json +++ b/backend/drizzle/meta/_journal.json @@ -176,6 +176,13 @@ "when": 1781891060177, "tag": "0024_skinny_iron_fist", "breakpoints": true + }, + { + "idx": 25, + "version": "7", + "when": 1781910033543, + "tag": "0025_nice_paladin", + "breakpoints": true } ] } \ No newline at end of file diff --git a/backend/src/db/schema/meetings.ts b/backend/src/db/schema/meetings.ts index 4e29e1f..e84a6df 100644 --- a/backend/src/db/schema/meetings.ts +++ b/backend/src/db/schema/meetings.ts @@ -1,4 +1,11 @@ -import { index, pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core"; +import { + index, + jsonb, + pgTable, + text, + timestamp, + uuid, +} from "drizzle-orm/pg-core"; import { organization, user } from "./auth.js"; @@ -21,3 +28,25 @@ export const meetingRooms = pgTable( }, (table) => [index("meeting_rooms_org_idx").on(table.organizationId)], ); + +// A scheduled staff meeting (calendar event), scoped to a clinic. `participants` +// holds the invited staff user ids; `date`/`time` are local strings (YYYY-MM-DD / +// HH:mm) like appointments, to avoid timezone drift on the calendar. +export const scheduledMeetings = pgTable( + "scheduled_meetings", + { + id: uuid("id").primaryKey().defaultRandom(), + organizationId: text("organization_id") + .notNull() + .references(() => organization.id, { onDelete: "cascade" }), + title: text("title").notNull(), + date: text("date").notNull(), + time: text("time").notNull(), + participants: jsonb("participants").$type().notNull().default([]), + createdBy: text("created_by").references(() => user.id, { + onDelete: "set null", + }), + createdAt: timestamp("created_at").defaultNow().notNull(), + }, + (table) => [index("scheduled_meetings_org_idx").on(table.organizationId)], +); diff --git a/backend/src/realtime.ts b/backend/src/realtime.ts index 76b164e..4e84d06 100644 --- a/backend/src/realtime.ts +++ b/backend/src/realtime.ts @@ -15,6 +15,7 @@ let io: Server | null = null; const userRoom = (userId: string) => `user:${userId}`; const convRoom = (conversationId: string) => `conv:${conversationId}`; const callRoom = (roomId: string) => `call:${roomId}`; +const orgRoom = (orgId: string) => `org:${orgId}`; // Mesh WebRTC tops out around four peers (each sends its stream to every other); // past that the room is closed to new joiners. @@ -69,8 +70,9 @@ export function initRealtime(httpServer: HttpServer): Server { const userName: string = socket.data.userName; const orgId: string | null = socket.data.orgId; - // Personal room for notifications. + // Personal room for notifications; clinic room for call presence broadcasts. socket.join(userRoom(userId)); + if (orgId) socket.join(orgRoom(orgId)); socket.on( "conversation:join", @@ -151,6 +153,14 @@ export function initRealtime(httpServer: HttpServer): Server { // org-scoped: a join is authorized against the clinic's meeting_rooms. const joinedCallRooms = new Set(); + // Broadcast a room's live occupancy to the whole clinic so the meetings + // room list can show "N in call". + const emitPresence = (roomId: string) => { + if (!orgId) return; + const count = callParticipants.get(roomId)?.size ?? 0; + io?.to(orgRoom(orgId)).emit("call:presence", { roomId, count }); + }; + const leaveCall = (roomId: string) => { if (!joinedCallRooms.has(roomId)) return; joinedCallRooms.delete(roomId); @@ -160,6 +170,7 @@ export function initRealtime(httpServer: HttpServer): Server { callParticipants.delete(roomId); } socket.to(callRoom(roomId)).emit("call:peer-left", { socketId: socket.id }); + emitPresence(roomId); }; socket.on("call:join", async (roomId: unknown, ack?: Ack) => { @@ -181,6 +192,7 @@ export function initRealtime(httpServer: HttpServer): Server { callParticipants.set(id, peers); // Tell existing peers a newcomer arrived; the newcomer initiates offers. socket.to(callRoom(id)).emit("call:peer-joined", me); + emitPresence(id); // Reply with the peers already present (excluding self). ack?.({ ok: true, @@ -191,6 +203,42 @@ export function initRealtime(httpServer: HttpServer): Server { } }); + // Ring a clinic member into a room: push a live invite + a bell notification. + socket.on( + "call:invite", + async (payload: { roomId?: string; toUserId?: string }) => { + try { + const roomId = String(payload?.roomId ?? ""); + const toUserId = String(payload?.toUserId ?? ""); + if (!(roomId && toUserId && orgId)) return; + if (!(await meetings.roomExists(orgId, roomId))) return; + const room = (await meetings.listRooms(orgId)).find( + (r) => r.id === roomId, + ); + const roomName = room?.name ?? ""; + emitToUser(toUserId, "call:invite", { + roomId, + roomName, + fromName: userName, + }); + const notification = await createNotification({ + orgId, + userId: toUserId, + type: "meeting", + text: `${userName} invited you to a call`, + entityType: "meeting", + entityId: roomId, + actorName: userName, + }); + if (notification) { + emitToUser(toUserId, "notification:new", notification); + } + } catch { + /* best-effort */ + } + }, + ); + // Relay an SDP offer/answer or ICE candidate to a specific peer socket. socket.on( "call:signal", @@ -217,6 +265,7 @@ export function initRealtime(httpServer: HttpServer): Server { socket .to(callRoom(roomId)) .emit("call:peer-left", { socketId: socket.id }); + emitPresence(roomId); } }); }); diff --git a/backend/src/routes/meetings.ts b/backend/src/routes/meetings.ts index 0287fba..b40cc05 100644 --- a/backend/src/routes/meetings.ts +++ b/backend/src/routes/meetings.ts @@ -39,6 +39,51 @@ meetingsRouter.post("/", async (req, res, next) => { } }); +// --- Scheduled meetings (calendar) ----------------------------------------- + +meetingsRouter.get("/events", async (req, res, next) => { + try { + res.json(await meetings.listMeetingEvents(req.organizationId!, req.user!.id)); + } catch (err) { + next(err); + } +}); + +const eventSchema = z.object({ + title: z.string().trim().min(1).max(120), + date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), + time: z.string().regex(/^\d{2}:\d{2}$/), + participants: z.array(z.string()).max(50).default([]), +}); + +meetingsRouter.post("/events", async (req, res, next) => { + try { + const input = eventSchema.parse(req.body); + const event = await meetings.createMeetingEvent( + req.organizationId!, + req.user!.id, + input, + ); + res.status(201).json(event); + } catch (err) { + next(err); + } +}); + +meetingsRouter.delete("/events/:id", async (req, res, next) => { + try { + const ok = await meetings.deleteMeetingEvent( + req.organizationId!, + req.user!.id, + String(req.params.id ?? ""), + ); + if (!ok) throw new HttpError(404, "Meeting not found."); + res.status(204).end(); + } catch (err) { + next(err); + } +}); + meetingsRouter.delete("/:id", async (req, res, next) => { try { const ok = await meetings.deleteRoom( diff --git a/backend/src/services/meetings.ts b/backend/src/services/meetings.ts index 823d6d9..07d495f 100644 --- a/backend/src/services/meetings.ts +++ b/backend/src/services/meetings.ts @@ -1,7 +1,8 @@ -import { and, asc, eq } from "drizzle-orm"; +import { and, asc, eq, inArray, or, sql } from "drizzle-orm"; import { db } from "../db/index.js"; -import { meetingRooms } from "../db/schema/meetings.js"; +import { user } from "../db/schema/auth.js"; +import { meetingRooms, scheduledMeetings } from "../db/schema/meetings.js"; export type MeetingRoom = { id: string; @@ -67,3 +68,99 @@ export async function roomExists(orgId: string, roomId: string): Promise { + const rows = await db + .select() + .from(scheduledMeetings) + .where( + and( + eq(scheduledMeetings.organizationId, orgId), + or( + eq(scheduledMeetings.createdBy, userId), + // `participants` is a JSONB array of user ids. + sql`${scheduledMeetings.participants} @> ${JSON.stringify([userId])}::jsonb`, + ), + ), + ) + .orderBy(asc(scheduledMeetings.date), asc(scheduledMeetings.time)); + + // Resolve participant names in one query. + const ids = [...new Set(rows.flatMap((r) => r.participants))]; + const nameById = new Map(); + if (ids.length > 0) { + const users = await db + .select({ id: user.id, name: user.name }) + .from(user) + .where(inArray(user.id, ids)); + for (const u of users) nameById.set(u.id, u.name); + } + + return rows.map((r) => ({ + id: r.id, + title: r.title, + date: r.date, + time: r.time, + participants: r.participants, + participantNames: r.participants.map((id) => nameById.get(id) ?? "—"), + createdBy: r.createdBy, + })); +} + +export async function createMeetingEvent( + orgId: string, + createdBy: string, + input: { title: string; date: string; time: string; participants: string[] }, +): Promise { + // The creator is always a participant. + const participants = [...new Set([createdBy, ...input.participants])]; + const [row] = await db + .insert(scheduledMeetings) + .values({ + organizationId: orgId, + title: input.title, + date: input.date, + time: input.time, + participants, + createdBy, + }) + .returning({ id: scheduledMeetings.id }); + const events = await listMeetingEvents(orgId, createdBy); + return events.find((e) => e.id === row!.id)!; +} + +export async function deleteMeetingEvent( + orgId: string, + userId: string, + id: string, +): Promise { + // Only the creator can delete. + const deleted = await db + .delete(scheduledMeetings) + .where( + and( + eq(scheduledMeetings.organizationId, orgId), + eq(scheduledMeetings.id, id), + eq(scheduledMeetings.createdBy, userId), + ), + ) + .returning({ id: scheduledMeetings.id }); + return deleted.length > 0; +} diff --git a/frontend/components/meetings/meeting-room.tsx b/frontend/components/meetings/meeting-room.tsx index c832307..5ae93e0 100644 --- a/frontend/components/meetings/meeting-room.tsx +++ b/frontend/components/meetings/meeting-room.tsx @@ -5,15 +5,35 @@ import { MicOff, MonitorUp, PhoneOff, + Search, + UserPlus, Video as VideoIcon, VideoOff, } from "lucide-react"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useSpeaking } from "@/components/meetings/use-audio-level"; import { useWebRtcMesh } from "@/components/meetings/use-webrtc-mesh"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogDescription, + DialogHeader, + DialogPanel, + DialogPopup, + DialogTitle, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { + Tooltip, + TooltipPopup, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { authClient } from "@/lib/auth-client"; +import { listClinicMembers, type Participant } from "@/lib/messages"; +import { getSocket } from "@/lib/socket"; import { cn } from "@/lib/utils"; function initials(name: string): string { @@ -23,7 +43,8 @@ function initials(name: string): string { return (parts[0]![0]! + parts.at(-1)![0]!).toUpperCase(); } -// One participant tile: shows their video, or an avatar when the camera is off. +// One participant tile: video when the camera is on, an avatar otherwise, with a +// green speaking ring driven by the stream's audio level. function VideoTile({ stream, label, @@ -36,13 +57,21 @@ function VideoTile({ showVideo: boolean; }) { const ref = useRef(null); + // Analyse the stream's audio for the speaking ring (muting only affects + // playback, so the local tile still gets a ring when you talk). + const speaking = useSpeaking(stream); useEffect(() => { if (ref.current && ref.current.srcObject !== stream) { ref.current.srcObject = stream; } }, [stream]); return ( -
+
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}