diff --git a/backend/drizzle/0029_tiny_starhawk.sql b/backend/drizzle/0029_tiny_starhawk.sql new file mode 100644 index 0000000..eaaffc3 --- /dev/null +++ b/backend/drizzle/0029_tiny_starhawk.sql @@ -0,0 +1 @@ +ALTER TABLE "wallet_share_requests" ALTER COLUMN "wallet_number" DROP NOT NULL; \ No newline at end of file diff --git a/backend/drizzle/meta/0029_snapshot.json b/backend/drizzle/meta/0029_snapshot.json new file mode 100644 index 0000000..f39906c --- /dev/null +++ b/backend/drizzle/meta/0029_snapshot.json @@ -0,0 +1,4300 @@ +{ + "id": "fbd92c91-eb3d-40eb-bde7-8ab53ab6493e", + "prevId": "802f6a65-bb4b-4eed-a6e3-8e566f962bc1", + "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'" + }, + "share_origin": { + "name": "share_origin", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "share_expires_at": { + "name": "share_expires_at", + "type": "timestamp", + "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": { + "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 + }, + "assignee_user_id": { + "name": "assignee_user_id", + "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_assignee_user_id_user_id_fk": { + "name": "tasks_assignee_user_id_user_id_fk", + "tableFrom": "tasks", + "tableTo": "user", + "columnsFrom": [ + "assignee_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "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.email_settings": { + "name": "email_settings", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "default": "'default'" + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "from_address": { + "name": "from_address", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "credentials": { + "name": "credentials", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "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 + }, + "public.clinic_signing_keys": { + "name": "clinic_signing_keys", + "schema": "", + "columns": { + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "algorithm": { + "name": "algorithm", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'ed25519'" + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fingerprint": { + "name": "fingerprint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key_enc": { + "name": "private_key_enc", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "rotated_at": { + "name": "rotated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "clinic_signing_keys_organization_id_organization_id_fk": { + "name": "clinic_signing_keys_organization_id_organization_id_fk", + "tableFrom": "clinic_signing_keys", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.wallet_share_requests": { + "name": "wallet_share_requests", + "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 + }, + "requested_by": { + "name": "requested_by", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "wallet_number": { + "name": "wallet_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ephemeral_pub_key": { + "name": "ephemeral_pub_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ephemeral_priv_enc": { + "name": "ephemeral_priv_enc", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "share_mode": { + "name": "share_mode", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'permanent'" + }, + "share_expires_at": { + "name": "share_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "draft": { + "name": "draft", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "committed_file_number": { + "name": "committed_file_number", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "resolved_at": { + "name": "resolved_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "wallet_share_org_idx": { + "name": "wallet_share_org_idx", + "columns": [ + { + "expression": "organization_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "wallet_share_wallet_idx": { + "name": "wallet_share_wallet_idx", + "columns": [ + { + "expression": "wallet_number", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "wallet_share_requests_organization_id_organization_id_fk": { + "name": "wallet_share_requests_organization_id_organization_id_fk", + "tableFrom": "wallet_share_requests", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "wallet_share_requests_requested_by_user_id_fk": { + "name": "wallet_share_requests_requested_by_user_id_fk", + "tableFrom": "wallet_share_requests", + "tableTo": "user", + "columnsFrom": [ + "requested_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "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 8250c36..0699535 100644 --- a/backend/drizzle/meta/_journal.json +++ b/backend/drizzle/meta/_journal.json @@ -204,6 +204,13 @@ "when": 1782052852524, "tag": "0028_military_cyclops", "breakpoints": true + }, + { + "idx": 29, + "version": "7", + "when": 1782057030557, + "tag": "0029_tiny_starhawk", + "breakpoints": true } ] } \ No newline at end of file diff --git a/backend/src/db/schema/wallet-share.ts b/backend/src/db/schema/wallet-share.ts index ccb374e..538d7bb 100644 --- a/backend/src/db/schema/wallet-share.ts +++ b/backend/src/db/schema/wallet-share.ts @@ -27,7 +27,10 @@ export const walletShareRequests = pgTable( requestedBy: text("requested_by") .notNull() .references(() => user.id, { onDelete: "cascade" }), - walletNumber: text("wallet_number").notNull(), + // Null for QR "scan to connect" pairing requests — the wallet number is + // bound when the authenticated device responds. Set up-front for the + // type-the-number push flow. + walletNumber: text("wallet_number"), ephemeralPubKey: text("ephemeral_pub_key").notNull(), // Encrypted (lib/crypto.ts) hex of the ephemeral X25519 private key. ephemeralPrivEnc: text("ephemeral_priv_enc").notNull(), diff --git a/backend/src/routes/patients-wallet.ts b/backend/src/routes/patients-wallet.ts index 479c43b..886030c 100644 --- a/backend/src/routes/patients-wallet.ts +++ b/backend/src/routes/patients-wallet.ts @@ -27,6 +27,33 @@ const requestSchema = z.object({ durationHours: z.number().positive().max(8760).optional(), }); +const pairSchema = z.object({ + mode: z.enum(["permanent", "temporary"]).default("permanent"), + durationHours: z.number().positive().max(8760).optional(), +}); + +// Create a QR pairing request (no wallet number yet). Returns the request id + +// the ephemeral public key the device seals its bundle to; the clinic encodes +// both — plus its own relay URL — into the QR the patient scans. +patientsWalletRouter.post( + "/pair", + requirePermission({ patient: ["write"] }), + async (req, res, next) => { + try { + const input = pairSchema.parse(req.body); + const { view, ephemeralPubKey } = await walletShare.createPairingRequest( + req.organizationId!, + req.user!.id, + input.mode, + input.durationHours, + ); + res.status(201).json({ ...view, ephemeralPubKey }); + } catch (err) { + next(err); + } + }, +); + // Start an import: validate the wallet number, mint a per-request ephemeral key, // and relay an encrypted-share request to the patient's device. The clinician // then polls the request until the patient approves on their phone. diff --git a/backend/src/services/wallet-share.ts b/backend/src/services/wallet-share.ts index aedd45b..cac91ca 100644 --- a/backend/src/services/wallet-share.ts +++ b/backend/src/services/wallet-share.ts @@ -33,7 +33,7 @@ export type ShareRequestView = { function toView(row: ShareRow): ShareRequestView { return { id: row.id, - walletNumber: row.walletNumber, + walletNumber: row.walletNumber ?? "", status: row.status, shareMode: row.shareMode, shareExpiresAt: row.shareExpiresAt ? row.shareExpiresAt.toISOString() : null, @@ -41,6 +41,35 @@ function toView(row: ShareRow): ShareRequestView { }; } +// Create a QR "scan to connect" pairing request — no wallet number yet (it is +// bound when the scanning device responds). Mints the ephemeral keypair the +// device seals to; the pairing URI (relay + id + ephemeral key) goes in the QR. +export async function createPairingRequest( + orgId: string, + userId: string, + mode: WalletShareMode, + durationHours?: number, +): Promise<{ view: ShareRequestView; ephemeralPubKey: string }> { + const { privateKeyHex, publicKeyHex } = newEncryptionKeypair(); + const shareExpiresAt = + mode === "temporary" && durationHours + ? new Date(Date.now() + durationHours * 3_600_000) + : null; + const [row] = await db + .insert(walletShareRequests) + .values({ + organizationId: orgId, + requestedBy: userId, + walletNumber: null, + ephemeralPubKey: publicKeyHex, + ephemeralPrivEnc: encryptSecret(privateKeyHex), + shareMode: mode, + shareExpiresAt, + }) + .returning(); + return { view: toView(row!), ephemeralPubKey: publicKeyHex }; +} + // Create an import request: validate the wallet number, mint a per-request // ephemeral X25519 keypair (the phone seals the bundle to its public key) and // store the request. Returns the row + the ephemeral public key to relay to the @@ -125,12 +154,19 @@ export async function applyShareResponse( .from(walletShareRequests) .where(eq(walletShareRequests.id, requestId)); if (!row || row.status !== "pending") return null; - if (row.walletNumber !== walletNumber.trim()) return null; + // Typed flow: the responder must match the wallet the clinic addressed. + // QR pairing flow (stored wallet number is null): bind it to the + // authenticated responder now. + if (row.walletNumber && row.walletNumber !== walletNumber.trim()) return null; if (decision === "denied") { const [updated] = await db .update(walletShareRequests) - .set({ status: "denied", resolvedAt: new Date() }) + .set({ + status: "denied", + resolvedAt: new Date(), + walletNumber: walletNumber.trim(), + }) .where(eq(walletShareRequests.id, requestId)) .returning(); return updated ? toView(updated) : null; @@ -150,7 +186,12 @@ export async function applyShareResponse( const [updated] = await db .update(walletShareRequests) - .set({ status: "approved", resolvedAt: new Date(), draft: bundle.patient }) + .set({ + status: "approved", + resolvedAt: new Date(), + draft: bundle.patient, + walletNumber: walletNumber.trim(), + }) .where(eq(walletShareRequests.id, requestId)) .returning(); return updated ? toView(updated) : null; diff --git a/frontend/components/patients/import-from-wallet-dialog.tsx b/frontend/components/patients/import-from-wallet-dialog.tsx index 23c72af..23ba97b 100644 --- a/frontend/components/patients/import-from-wallet-dialog.tsx +++ b/frontend/components/patients/import-from-wallet-dialog.tsx @@ -1,8 +1,9 @@ "use client"; -import { Check, Loader2, Smartphone, X } from "lucide-react"; +import { Check, Loader2, QrCode, Smartphone, X } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; +import QRCodeSvg from "react-qr-code"; import { PatientFormDialog } from "@/components/chat/patient-form-dialog"; import { Button } from "@/components/ui/button"; @@ -18,11 +19,12 @@ import { } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; -import { ApiError } from "@/lib/api-client"; +import { ApiError, API_BASE_URL } from "@/lib/api-client"; import { commitWalletShare, type Patient, pollWalletShare, + requestWalletPairing, requestWalletShare, type WalletShareRequest, } from "@/lib/patients"; @@ -38,6 +40,8 @@ type Phase = | "expired" | "error"; +type Mode = "number" | "qr"; + const DURATIONS = [ { hours: 1, key: "hours", count: 1 }, { hours: 24, key: "days", count: 1 }, @@ -57,12 +61,14 @@ export function ImportFromWalletDialog({ onImported?: (fileNumber: string) => void; }) { const { t } = useTranslation(); + const [mode, setMode] = useState("number"); const [walletNumber, setWalletNumber] = useState(""); const [temporary, setTemporary] = useState(false); const [durationHours, setDurationHours] = useState(24); const [phase, setPhase] = useState("form"); const [error, setError] = useState(null); const [request, setRequest] = useState(null); + const [pairUri, setPairUri] = useState(null); const [reviewOpen, setReviewOpen] = useState(false); const pollTimer = useRef | null>(null); @@ -76,12 +82,14 @@ export function ImportFromWalletDialog({ // Reset everything whenever the dialog is (re)opened. useEffect(() => { if (open) { + setMode("number"); setWalletNumber(""); setTemporary(false); setDurationHours(24); setPhase("form"); setError(null); setRequest(null); + setPairUri(null); setReviewOpen(false); } return stopPolling; @@ -140,6 +148,32 @@ export function ImportFromWalletDialog({ } }; + // QR flow: create a pairing request (no wallet number) and build the + // `temetro-pair:` URI the app scans (this clinic's relay URL + request + key). + const startPairing = async () => { + setPhase("requesting"); + setError(null); + try { + const pairing = await requestWalletPairing({ + mode: temporary ? "temporary" : "permanent", + durationHours: temporary ? durationHours : undefined, + }); + const params = new URLSearchParams({ + relay: API_BASE_URL, + rid: pairing.id, + epk: pairing.ephemeralPubKey, + mode: pairing.shareMode, + }); + if (temporary) params.set("dur", String(durationHours)); + setPairUri(`temetro-pair:?${params.toString()}`); + setRequest(pairing); + setPhase("waiting"); + } catch { + setError(t("patients.importApp.error")); + setPhase("error"); + } + }; + const commitDraft = async (record: Patient) => { if (!request) return; try { @@ -177,7 +211,20 @@ export function ImportFromWalletDialog({ - {phase === "waiting" ? ( + {phase === "waiting" && mode === "qr" && pairUri ? ( +
+
+ +
+

+ {t("patients.importApp.qrCaption")} +

+

+ + {t("patients.importApp.waitingTitle")} +

+
+ ) : phase === "waiting" ? (

@@ -212,18 +259,47 @@ export function ImportFromWalletDialog({

) : ( <> - +
+ + +
+ + {mode === "number" ? ( + + ) : ( +

+ {t("patients.importApp.qrHint")} +

+ )}
@@ -282,7 +358,8 @@ export function ImportFromWalletDialog({ ? t("patients.importApp.close") : t("patients.importApp.cancel")} - {phase === "form" || phase === "requesting" || phase === "error" ? ( + {(phase === "form" || phase === "requesting" || phase === "error") && + mode === "number" ? ( ) : null} diff --git a/frontend/lib/i18n/locales/en/translation.json b/frontend/lib/i18n/locales/en/translation.json index c961bf6..f6fe1c6 100644 --- a/frontend/lib/i18n/locales/en/translation.json +++ b/frontend/lib/i18n/locales/en/translation.json @@ -186,6 +186,11 @@ "importApp": { "title": "Import from a patient app", "description": "Enter the patient's wallet number. They'll get a request on their phone to approve sharing their record with this clinic.", + "tabNumber": "By number", + "tabQr": "Show QR", + "qrHint": "Generate a QR code, then have the patient scan it in their wallet app to share their record.", + "qrCaption": "Scan this in the patient app", + "generateQr": "Show QR code", "walletLabel": "Patient wallet number", "walletPlaceholder": "tmw_…", "tempLabel": "Share temporarily", diff --git a/frontend/lib/patients.ts b/frontend/lib/patients.ts index 73ec465..4389b5d 100644 --- a/frontend/lib/patients.ts +++ b/frontend/lib/patients.ts @@ -213,6 +213,21 @@ export async function requestWalletShare(input: { }); } +// Create a QR pairing request (no wallet number — the patient scans a QR that +// carries this clinic's relay URL + the request). Returns the request plus the +// ephemeral public key the device seals to. +export type WalletPairing = WalletShareRequest & { ephemeralPubKey: string }; + +export async function requestWalletPairing(input: { + mode: WalletShareMode; + durationHours?: number; +}): Promise { + return apiFetch("/api/patients/wallet/pair", { + method: "POST", + body: JSON.stringify(input), + }); +} + // Poll a request until the patient approves/denies on their device. export async function pollWalletShare( id: string, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1f5e145..0104363 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -55,6 +55,7 @@ "react-dom": "19.2.4", "react-i18next": "^17.0.8", "react-jsx-parser": "^2.4.1", + "react-qr-code": "^2.2.0", "shadcn": "^4.8.3", "shiki": "^4.1.0", "socket.io-client": "^4.8.3", @@ -10580,7 +10581,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -12805,7 +12805,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -12985,6 +12984,12 @@ "node": ">=6" } }, + "node_modules/qrcode-generator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-2.0.4.tgz", + "integrity": "sha512-mZSiP6RnbHl4xL2Ap5HfkjLnmxfKcPWpWe/c+5XxCuetEenqmNFf1FH/ftXPCtFG5/TDobjsjz6sSNL0Sr8Z9g==", + "license": "MIT" + }, "node_modules/qs": { "version": "6.15.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", @@ -13122,7 +13127,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-jsx-parser": { @@ -13167,6 +13171,19 @@ "@types/react": "^18.0.0" } }, + "node_modules/react-qr-code": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/react-qr-code/-/react-qr-code-2.2.0.tgz", + "integrity": "sha512-e5nS0UUN22K3Nf8KBRUzemfdJ6OmnN5w+kbnj1lvJaol9RyVRFeGl05bCkxSN2ZegbLxjjYjX1+mmAoX9+fAhw==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.8.1", + "qrcode-generator": "^2.0.4" + }, + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-remove-scroll": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index c71e290..95a2896 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -56,6 +56,7 @@ "react-dom": "19.2.4", "react-i18next": "^17.0.8", "react-jsx-parser": "^2.4.1", + "react-qr-code": "^2.2.0", "shadcn": "^4.8.3", "shiki": "^4.1.0", "socket.io-client": "^4.8.3",