diff --git a/app/background/clinician-info-form.tsx b/app/background/clinician-info-form.tsx new file mode 100644 index 0000000..fd0adea --- /dev/null +++ b/app/background/clinician-info-form.tsx @@ -0,0 +1,99 @@ +"use client"; + +import { toast } from "@/components/ui/use-toast"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; +import { useRouter } from "next/navigation"; +import { type BaseSyntheticEvent } from "react"; +import { useForm } from "react-hook-form"; + +import { type Database } from "@/lib/schema"; +import { clinicianSchema, type ClinicianData } from "./info-schemas"; +type Clinician = Database["public"]["Tables"]["clinicians"]["Row"]; + +export default function ClinicianInfoForm(clinician: Clinician) { + const router = useRouter(); + + const defaultValues: Partial = { + first_name: clinician.first_name ?? undefined, + last_name: clinician.last_name ?? undefined, + employer: clinician.employer ?? undefined, + state: clinician.state ?? undefined, + city: clinician.city ?? undefined, + zip: clinician.zip ?? undefined, + }; + + const form = useForm({ + resolver: zodResolver(clinicianSchema), + defaultValues, + mode: "onChange", + }); + + const onSubmit = async (input: ClinicianData) => { + const supabase = createClientComponentClient(); + const { error } = await supabase + .from("clinicians") + .update({ + user_id: clinician.user_id, + ...input, + }) + .eq("id", clinician.id); + + if (error) { + return toast({ + title: "Something went wrong.", + description: error.message, + variant: "destructive", + }); + } + + form.reset(input); + router.refresh(); + return toast({ + title: "Success!", + description: "Your information has been updated.", + variant: "default", + }); + }; + + return ( +
void form.handleSubmit(onSubmit)(e)}> +
+ + + {form.formState.errors.first_name && (Error: {form.formState.errors.first_name?.message})} +
+
+ + + {form.formState.errors.last_name && (Error: {form.formState.errors.last_name?.message})} +
+
+ + + {form.formState.errors.employer && (Error: {form.formState.errors.employer?.message})} +
+
+ + + {form.formState.errors.state && (Error: {form.formState.errors.state?.message})} +
+
+ + + {form.formState.errors.city && (Error: {form.formState.errors.city?.message})} +
+
+ + + {form.formState.errors.zip && (Error: {form.formState.errors.zip?.message})} +
+
+
+ +
+
+ ); +} diff --git a/app/background/info-schemas.ts b/app/background/info-schemas.ts new file mode 100644 index 0000000..041f287 --- /dev/null +++ b/app/background/info-schemas.ts @@ -0,0 +1,62 @@ +import { z } from "zod"; + +const patientSchema = z.object({ + first_name: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + last_name: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + age: z.coerce + .number() + .int() + .gte(0) + .nullable() + .transform((val) => (val == 0 ? null : val)), + state: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + city: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + zip: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), +}); + +const clinicianSchema = z.object({ + first_name: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + last_name: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + employer: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + state: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + city: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), + zip: z + .string() + .nullable() + .transform((val) => (val?.trim() === "" ? null : val?.trim())), +}); + +type PatientData = z.infer; +type ClinicianData = z.infer; + +export { clinicianSchema, patientSchema, type ClinicianData, type PatientData }; diff --git a/app/background/page.tsx b/app/background/page.tsx new file mode 100644 index 0000000..0c421df --- /dev/null +++ b/app/background/page.tsx @@ -0,0 +1,32 @@ +import { createServerSupabaseClient } from "@/lib/server-utils"; +import { redirect } from "next/navigation"; +import ClinicianInfoForm from "./clinician-info-form"; +import PatientInfoForm from "./patient-info-form"; + +export default async function BackgroundInfo() { + const supabase = createServerSupabaseClient(); + const { + data: { session }, + } = await supabase.auth.getSession(); + + if (!session) { + redirect("/"); + } + + // Retrieve user information + const userid = session.user.id; + const { data: patients } = await supabase.from("patients").select().eq("user_id", userid); + const { data: clinicians } = await supabase.from("clinicians").select().eq("user_id", userid); + + // Allow size > 1 for now + if (!patients?.length && !clinicians?.length) { + return <>User data not found.; + } + + return ( + <> + {patients?.map((patient) => )} + {clinicians?.map((clinician) => )} + + ); +} diff --git a/app/background/patient-info-form.tsx b/app/background/patient-info-form.tsx new file mode 100644 index 0000000..48e87f4 --- /dev/null +++ b/app/background/patient-info-form.tsx @@ -0,0 +1,94 @@ +"use client"; + +import { toast } from "@/components/ui/use-toast"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { createClientComponentClient } from "@supabase/auth-helpers-nextjs"; +import { useRouter } from "next/navigation"; +import { type BaseSyntheticEvent } from "react"; +import { useForm } from "react-hook-form"; + +import { type Database } from "@/lib/schema"; +import { patientSchema, type PatientData } from "./info-schemas"; +type Patient = Database["public"]["Tables"]["patients"]["Row"]; + +export default function PatientInfoForm(patient: Patient) { + const router = useRouter(); + + const defaultValues: Partial = { + first_name: patient.first_name ?? undefined, + last_name: patient.last_name ?? undefined, + age: patient.age ?? undefined, + state: patient.state ?? undefined, + city: patient.city ?? undefined, + zip: patient.zip ?? undefined, + }; + + const form = useForm({ + resolver: zodResolver(patientSchema), + defaultValues, + mode: "onChange", + }); + + const onSubmit = async (input: PatientData) => { + const supabase = createClientComponentClient(); + const { error } = await supabase + .from("patients") + .update({ + user_id: patient.user_id, + ...input, + }) + .eq("id", patient.id); + + if (error) { + return toast({ + title: "Something went wrong.", + description: error.message, + variant: "destructive", + }); + } + + form.reset(input); + router.refresh(); + }; + + return ( +
void form.handleSubmit(onSubmit)(e)}> +
+ + + {form.formState.errors.first_name && (Error: {form.formState.errors.first_name?.message})} +
+
+ + + {form.formState.errors.last_name && (Error: {form.formState.errors.last_name?.message})} +
+
+ + + {form.formState.errors.age && (Error: {form.formState.errors.age?.message})} +
+
+ + + {form.formState.errors.state && (Error: {form.formState.errors.state?.message})} +
+
+ + + {form.formState.errors.city && (Error: {form.formState.errors.city?.message})} +
+
+ + + {form.formState.errors.zip && (Error: {form.formState.errors.zip?.message})} +
+
+
+ +
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx index 18278e2..dff3b4f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,18 +1,19 @@ import { TypographyH2, TypographyP } from "@/components/ui/typography"; +import { createServerSupabaseClient } from "@/lib/server-utils"; +import { redirect } from "next/navigation"; -export default function Home() { +export default async function Home() { + // Create supabase server component client and obtain user session from stored cookie + const supabase = createServerSupabaseClient(); + const { + data: { session }, + } = await supabase.auth.getSession(); + + if (!session) { + // Users who are already signed in should be redirected to dashboard + redirect("/login"); + } return ( - <> - Welcome to team Therable! - If you're seeing this, your setup has (most likely) gone well! - - This starter project is styled with Tailwind CSS and uses shadcn/ui as a component library. Things should feel - familiar, this is what your deliverable (if you're new here) was built on! - - - This page is an unprotected route accessible to anyone who visits the website. Log in to view authenticated - routes! - - + Welcome to Therable! ); } diff --git a/components/email-password-form.tsx b/components/email-password-form.tsx index dd2f122..518e83a 100644 --- a/components/email-password-form.tsx +++ b/components/email-password-form.tsx @@ -73,7 +73,7 @@ export function UserAuthFormUI({ _onSubmit, isLoading, buttonDisplay, className, ; + Returns: unknown; + }; }; Enums: { [_ in never]: never; diff --git a/package-lock.json b/package-lock.json index 977b1b2..32acfd2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -280,16 +280,27 @@ "glob": "7.1.7" } }, +<<<<<<< HEAD "node_modules/@next/swc-win32-x64-msvc": { "version": "13.4.19", "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz", "integrity": "sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==", +======= + "node_modules/@next/swc-darwin-x64": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz", + "integrity": "sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==", +>>>>>>> 3b4b51f3cf7a5338b689cd9b8c9435e0807e848f "cpu": [ "x64" ], "optional": true, "os": [ +<<<<<<< HEAD "win32" +======= + "darwin" +>>>>>>> 3b4b51f3cf7a5338b689cd9b8c9435e0807e848f ], "engines": { "node": ">= 10" @@ -379,9 +390,9 @@ } }, "node_modules/@radix-ui/react-avatar": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.0.4.tgz", - "integrity": "sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.0.3.tgz", + "integrity": "sha512-9ToF7YNex3Ste45LrAeTlKtONI9yVRt/zOS158iilIkW5K/Apeyb/TUQlcEFTEFvWr8Kzdi2ZYrm1/suiXPajQ==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/react-context": "1.0.1", @@ -509,16 +520,16 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", - "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.5.tgz", + "integrity": "sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-menu": "2.0.6", + "@radix-ui/react-menu": "2.0.5", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-use-controllable-state": "1.0.1" }, @@ -621,9 +632,9 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", - "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.5.tgz", + "integrity": "sha512-Gw4f9pwdH+w5w+49k0gLjN0PfRDHvxmAgG16AbyJZ7zhwZ6PBHKtWohvnSwfusfnK3L68dpBREHpVkj8wEM7ZA==", "dependencies": { "@babel/runtime": "^7.13.10", "@radix-ui/primitive": "1.0.1", @@ -631,12 +642,12 @@ "@radix-ui/react-compose-refs": "1.0.1", "@radix-ui/react-context": "1.0.1", "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-dismissable-layer": "1.0.4", "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-focus-scope": "1.0.3", "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-popper": "1.1.2", + "@radix-ui/react-portal": "1.0.3", "@radix-ui/react-presence": "1.0.1", "@radix-ui/react-primitive": "1.0.3", "@radix-ui/react-roving-focus": "1.0.4", @@ -660,6 +671,7 @@ } } }, +<<<<<<< HEAD "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", @@ -767,6 +779,8 @@ } } }, +======= +>>>>>>> 3b4b51f3cf7a5338b689cd9b8c9435e0807e848f "node_modules/@radix-ui/react-popper": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", @@ -6198,6 +6212,129 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } +<<<<<<< HEAD +======= + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz", + "integrity": "sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.19.tgz", + "integrity": "sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.19.tgz", + "integrity": "sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.19.tgz", + "integrity": "sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.19.tgz", + "integrity": "sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.19.tgz", + "integrity": "sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.19.tgz", + "integrity": "sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "13.4.19", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz", + "integrity": "sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } +>>>>>>> 3b4b51f3cf7a5338b689cd9b8c9435e0807e848f } } -} +} \ No newline at end of file diff --git a/setup.sql b/setup.sql index 8002f4f..427a6e2 100644 --- a/setup.sql +++ b/setup.sql @@ -1,129 +1,129 @@ --- Create a table for patients -create table patients ( - id uuid not null primary key, - user_id uuid not null references auth.users(id), - first_name text, - last_name text, - age integer, - state text, - city text, - zip text -); - --- Create a table for clinicians -create table clinicians ( - id uuid not null primary key, - user_id uuid references auth.users(id), - first_name text, - last_name text, - employer text, - state text, - city text, - zip text -); - --- Create a table for admins -create table admins ( - id uuid not null primary key, - user_id uuid not null references auth.users(id) -); - --- Create a table for clinics - this was being problematic when putting it into supabse -create table clinics ( - id uuid not null primary key, - owner uuid references clinicians(id) not null, - code text not null, - name text not null -); - --- Create a table for messages -create table messages ( - id uuid not null primary key, - sender uuid references auth.users(id), - receiver uuid references auth.users(id), - message text not null, - media text -); - --- Create a table for tasks -create table tasks ( - id uuid not null primary key, - assigner uuid references clinicians(id), - patient uuid references patients(id), - name text not null, - description text, - media text, - assign_date timestamp not null, - due_date timestamp, - completed boolean not null, - complete_date timestamp not null -); - --- Create a table for clinic members -create table clinic_members ( - id uuid not null primary key, - patient_id uuid references patients(id), - clinic_id uuid references clinics(id), - diagnosis text not null, - join_date timestamp not null -); - --- Create a table for milestones -create table milestones ( - id uuid not null primary key, - assigner uuid references clinicians(id), - patient uuid references patients(id), - clinic_id uuid references clinics(id), - name text not null, - description text -); - --- -- Set up Row Level Security (RLS) --- -- See https://supabase.com/docs/guides/auth/row-level-security for more details. - --- Enable Row Level Security for patients, clinicians, admins, clinics, messages, tasks, clinicMembers, and milestones tables --- ALTER TABLE patients ENABLE ROW LEVEL SECURITY; --- ALTER TABLE clinicians ENABLE ROW LEVEL SECURITY; --- ALTER TABLE admins ENABLE ROW LEVEL SECURITY; --- ALTER TABLE clinics ENABLE ROW LEVEL SECURITY; --- ALTER TABLE messages ENABLE ROW LEVEL SECURITY; --- ALTER TABLE tasks ENABLE ROW LEVEL SECURITY; --- ALTER TABLE clinicMembers ENABLE ROW LEVEL SECURITY; --- ALTER TABLE milestones ENABLE ROW LEVEL SECURITY; - --- -- Allow users to view/edit their own data in the patients, clinicians, and admins tables --- create policy "Users can view their own data." on users --- for select using (auth.uid() = userId); - --- create policy "Patients can view their own data." on patients --- for select using (auth.uid() = userId); - --- create policy "Clinicians can view their own data." on patients --- for select using (auth.uid() = clinicianId); - --- This trigger automatically creates a profile entry when a new user signs up via Supabase Auth. --- See https://supabase.com/docs/guides/auth/managing-user-data#using-triggers for more details. -create function public.handle_new_user() -returns trigger as $$ -DECLARE - username text; -BEGIN - SELECT substring(NEW.email from '(.*)@') INTO username; - - INSERT INTO public.profiles (id, email, display_name, biography) - VALUES (NEW.id, NEW.id, username, ''); - - IF NEW.raw_user_meta_data->>'type' = 'patient' THEN - INSERT INTO public.patients (user_id, first_name, last_name, age, state, city, zip) - VALUES (NEW.id, NEW.raw_user_meta_data ->>'first_name', NEW.raw_user_meta_data->>'last_name', (NEW.raw_user_meta_data->>'age')::integer, NEW.raw_user_meta_data->>'state', NEW.raw_user_meta_data->>'city', NEW.raw_user_meta_data->>'zipcode'); - ELSIF NEW.raw_user_meta_data->>'type' = 'clinician' THEN - INSERT INTO public.clinicians (user_id, first_name, last_name, employer, state, city, zip) - VALUES (NEW.id, NEW.raw_user_meta_data->>'first_name', NEW.raw_user_meta_data->>'last_name', NEW.raw_user_meta_data->>'employer', NEW.raw_user_meta_data->>'state', NEW.raw_user_meta_data->>'city', NEW.raw_user_meta_data->>'zipcode'); - END IF; - - RETURN NEW; -END; -$$ language plpgsql security definer; -create trigger on_auth_user_created - after insert on auth.users - for each row execute procedure public.handle_new_user(); +-- Create a table for patients +create table patients ( + id uuid not null primary key, + user_id uuid not null references auth.users(id), + first_name not null text, + last_name not null text, + age integer, + state text, + city text, + zip text +); + +-- Create a table for clinicians +create table clinicians ( + id uuid not null primary key, + user_id uuid references auth.users(id), + first_name not null text, + last_name not null text, + employer text, + state text, + city text, + zip text +); + +-- Create a table for admins +create table admins ( + id uuid not null primary key, + user_id uuid not null references auth.users(id) +); + +-- Create a table for clinics - this was being problematic when putting it into supabse +create table clinics ( + id uuid not null primary key, + owner uuid references clinicians(id) not null, + code text not null, + name text not null +); + +-- Create a table for messages +create table messages ( + id uuid not null primary key, + sender uuid references auth.users(id), + receiver uuid references auth.users(id), + message text not null, + media text +); + +-- Create a table for tasks +create table tasks ( + id uuid not null primary key, + assigner uuid references clinicians(id), + patient uuid references patients(id), + name text not null, + description text, + media text, + assign_date timestamp not null, + due_date timestamp, + completed boolean not null, + complete_date timestamp not null +); + +-- Create a table for clinic members +create table clinic_members ( + id uuid not null primary key, + patient_id uuid references patients(id), + clinic_id uuid references clinics(id), + diagnosis text not null, + join_date timestamp not null +); + +-- Create a table for milestones +create table milestones ( + id uuid not null primary key, + assigner uuid references clinicians(id), + patient uuid references patients(id), + clinic_id uuid references clinics(id), + name text not null, + description text +); + +-- -- Set up Row Level Security (RLS) +-- -- See https://supabase.com/docs/guides/auth/row-level-security for more details. + +-- Enable Row Level Security for patients, clinicians, admins, clinics, messages, tasks, clinicMembers, and milestones tables +-- ALTER TABLE patients ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE clinicians ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE admins ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE clinics ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE messages ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE tasks ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE clinicMembers ENABLE ROW LEVEL SECURITY; +-- ALTER TABLE milestones ENABLE ROW LEVEL SECURITY; + +-- -- Allow users to view/edit their own data in the patients, clinicians, and admins tables +-- create policy "Users can view their own data." on users +-- for select using (auth.uid() = userId); + +-- create policy "Patients can view their own data." on patients +-- for select using (auth.uid() = userId); + +-- create policy "Clinicians can view their own data." on patients +-- for select using (auth.uid() = clinicianId); + +-- This trigger automatically creates a profile entry when a new user signs up via Supabase Auth. +-- See https://supabase.com/docs/guides/auth/managing-user-data#using-triggers for more details. +create function public.handle_new_user() +returns trigger as $$ +DECLARE + username text; +BEGIN + SELECT substring(NEW.email from '(.*)@') INTO username; + + INSERT INTO public.profiles (id, email, display_name, biography) + VALUES (NEW.id, NEW.id, username, ''); + + IF NEW.raw_user_meta_data->>'type' = 'patient' THEN + INSERT INTO public.patients (user_id, first_name, last_name, age, state, city, zip) + VALUES (NEW.id, NEW.raw_user_meta_data ->>'first_name', NEW.raw_user_meta_data->>'last_name', (NEW.raw_user_meta_data->>'age')::integer, NEW.raw_user_meta_data->>'state', NEW.raw_user_meta_data->>'city', NEW.raw_user_meta_data->>'zipcode'); + ELSIF NEW.raw_user_meta_data->>'type' = 'clinician' THEN + INSERT INTO public.clinicians (user_id, first_name, last_name, employer, state, city, zip) + VALUES (NEW.id, NEW.raw_user_meta_data->>'first_name', NEW.raw_user_meta_data->>'last_name', NEW.raw_user_meta_data->>'employer', NEW.raw_user_meta_data->>'state', NEW.raw_user_meta_data->>'city', NEW.raw_user_meta_data->>'zipcode'); + END IF; + + RETURN NEW; +END; +$$ language plpgsql security definer; +create trigger on_auth_user_created + after insert on auth.users + for each row execute procedure public.handle_new_user(); \ No newline at end of file