From 3269e506a17ecb9d78faaa07b8bdc29328b81603 Mon Sep 17 00:00:00 2001 From: cade Date: Fri, 18 Oct 2024 18:21:36 -0700 Subject: [PATCH] add teams --- README.md | 1 + app/(pages)/(with-nav)/layout.tsx | 23 +- app/(pages)/(with-nav)/subjects/page.tsx | 10 +- app/(pages)/@modal/(layout)/hey/page.tsx | 30 +- .../subjects/[subjectId]/edit/page.tsx | 12 +- .../event-types/[eventTypeId]/page.tsx | 4 +- .../sessions/[sessionId]/edit/page.tsx | 3 +- .../[order]/from-session/[sessionId]/page.tsx | 3 +- .../sessions/create/[order]/page.tsx | 3 +- .../@modal/(layout)/subjects/create/page.tsx | 18 +- .../(layout)/teams/[teamId]/edit/loading.tsx | 5 + .../(layout)/teams/[teamId]/edit/page.tsx | 23 + .../@modal/(layout)/teams/create/loading.tsx | 5 + .../@modal/(layout)/teams/create/page.tsx | 12 + .../[subjectId]/join/[shareCode]/page.tsx | 4 +- app/_components/account-menu.tsx | 163 ++- app/_components/account-profile-form.tsx | 34 +- app/_components/avatar-dropzone.tsx | 2 +- app/_components/checkbox.tsx | 6 +- app/_components/drawer.tsx | 45 +- app/_components/empty.tsx | 2 +- app/_components/event-form.tsx | 53 +- app/_components/event-page.tsx | 3 +- app/_components/event-select.tsx | 69 +- app/_components/event-type-form.tsx | 2 +- app/_components/event-type-template-form.tsx | 2 +- .../event-type-use-template-drawer.tsx | 2 +- app/_components/icon-button.tsx | 74 +- app/_components/input-form.tsx | 2 +- app/_components/insight-form.tsx | 2 +- app/_components/insights-page.tsx | 3 +- app/_components/module-card.tsx | 2 +- app/_components/module-form-section.tsx | 2 +- app/_components/module-template-form.tsx | 2 +- .../module-use-template-drawer.tsx | 2 +- .../protocol-use-template-drawer.tsx | 2 +- app/_components/protocols.tsx | 4 +- app/_components/{select.tsx => select-v1.tsx} | 0 app/_components/select-v2.tsx | 253 ++++ app/_components/session-page.tsx | 3 +- .../session-use-template-drawer.tsx | 2 +- app/_components/sessions-page.tsx | 3 +- app/_components/sign-up-form.tsx | 21 +- app/_components/subject-form.tsx | 82 +- app/_components/subject-page.tsx | 18 +- app/_components/team-form.tsx | 115 ++ app/_components/template-form-section.tsx | 2 +- app/_components/toggle-group.tsx | 47 + app/_constants/enum-team-member-role.ts | 8 + app/_mutations/create-tag.ts | 17 + app/_mutations/set-active-team.ts | 32 + app/_mutations/sign-up.ts | 40 +- app/_mutations/update-subject.ts | 3 +- app/_mutations/upsert-avatar.ts | 25 + app/_mutations/upsert-event-type-template.ts | 3 + app/_mutations/upsert-event.ts | 11 +- app/_mutations/upsert-input.ts | 3 + app/_mutations/upsert-module-template.ts | 3 + app/_mutations/upsert-protocol-template.ts | 3 + app/_mutations/upsert-session-template.ts | 3 + app/_mutations/upsert-subject.ts | 16 +- app/_mutations/upsert-team.ts | 23 + app/_queries/count-notifications.ts | 4 +- app/_queries/get-input-with-uses.ts | 2 +- app/_queries/get-input.ts | 2 +- app/_queries/get-subject.ts | 13 +- app/_queries/get-team.ts | 12 + app/_queries/get-template-data.ts | 2 +- app/_queries/get-template.ts | 2 +- app/_queries/list-inputs-by-subject-id.ts | 4 +- app/_queries/list-inputs-with-uses.ts | 4 +- app/_queries/list-inputs.ts | 4 +- app/_queries/list-notifications.ts | 2 + app/_queries/list-subject-managers.ts | 14 - app/_queries/list-subjects-by-team-id.ts | 2 +- app/_queries/list-subjects.ts | 29 +- app/_queries/list-tags.ts | 13 + app/_queries/list-teams.ts | 25 + .../list-templates-by-subject-id-and-type.ts | 4 +- app/_queries/list-templates.ts | 7 +- app/_types/multi-select-input-type.ts | 3 - app/_types/select-input-type.ts | 3 - .../get-insight-options-from-events.ts | 2 +- bun.lockb | Bin 369188 -> 361612 bytes package.json | 74 +- .../20241011171810_add-team-functionality.sql | 1260 +++++++++++++++++ tailwind.css | 3 +- 87 files changed, 2473 insertions(+), 387 deletions(-) create mode 100644 app/(pages)/@modal/(layout)/teams/[teamId]/edit/loading.tsx create mode 100644 app/(pages)/@modal/(layout)/teams/[teamId]/edit/page.tsx create mode 100644 app/(pages)/@modal/(layout)/teams/create/loading.tsx create mode 100644 app/(pages)/@modal/(layout)/teams/create/page.tsx rename app/_components/{select.tsx => select-v1.tsx} (100%) create mode 100644 app/_components/select-v2.tsx create mode 100644 app/_components/team-form.tsx create mode 100644 app/_components/toggle-group.tsx create mode 100644 app/_constants/enum-team-member-role.ts create mode 100644 app/_mutations/create-tag.ts create mode 100644 app/_mutations/set-active-team.ts create mode 100644 app/_mutations/upsert-avatar.ts create mode 100644 app/_mutations/upsert-team.ts create mode 100644 app/_queries/get-team.ts delete mode 100644 app/_queries/list-subject-managers.ts create mode 100644 app/_queries/list-tags.ts create mode 100644 app/_queries/list-teams.ts delete mode 100644 app/_types/multi-select-input-type.ts delete mode 100644 app/_types/select-input-type.ts create mode 100644 supabase/migrations/20241011171810_add-team-functionality.sql diff --git a/README.md b/README.md index 8218610d..2f9779f3 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Add the following to your `.env` file: ```dotenv NEXT_PUBLIC_SUPABASE_ANON_KEY= NEXT_PUBLIC_SUPABASE_URL= +SUPABASE_SERVICE_KEY= ``` Generate types and start the dev server: diff --git a/app/(pages)/(with-nav)/layout.tsx b/app/(pages)/(with-nav)/layout.tsx index ef344971..9d79a651 100644 --- a/app/(pages)/(with-nav)/layout.tsx +++ b/app/(pages)/(with-nav)/layout.tsx @@ -7,6 +7,7 @@ import NotificationsSubscription from '@/_components/notifications-subscription' import canInsertSubjectOnCurrentPlan from '@/_queries/can-insert-subject-on-current-plan'; import countNotifications from '@/_queries/count-notifications'; import getCurrentUser from '@/_queries/get-current-user'; +import listTeams from '@/_queries/list-teams'; import Bars3Icon from '@heroicons/react/24/outline/Bars3Icon'; import DocumentTextIcon from '@heroicons/react/24/outline/DocumentTextIcon'; import HomeIcon from '@heroicons/react/24/outline/HomeIcon'; @@ -20,14 +21,20 @@ interface LayoutProps { } const Layout = async ({ children }: LayoutProps) => { - const [{ data: canCreateSubject }, { count }, user] = await Promise.all([ - canInsertSubjectOnCurrentPlan(), - countNotifications(), - getCurrentUser(), - ]); - + const user = await getCurrentUser(); if (!user) return null; + const [{ data: canCreateSubject }, { count }, { data: teams }] = + await Promise.all([ + user.app_metadata.is_client + ? Promise.resolve({ data: false }) + : canInsertSubjectOnCurrentPlan(), + countNotifications(), + user.app_metadata.is_client ? Promise.resolve({ data: [] }) : listTeams(), + ]); + + if (!teams) return null; + return ( <>
@@ -61,7 +68,7 @@ const Layout = async ({ children }: LayoutProps) => {
Inbox - {!user.user_metadata.is_client && ( + {!user.app_metadata.is_client && ( <> @@ -109,7 +116,7 @@ const Layout = async ({ children }: LayoutProps) => { )} - + diff --git a/app/(pages)/(with-nav)/subjects/page.tsx b/app/(pages)/(with-nav)/subjects/page.tsx index ebb46047..bf1046f5 100644 --- a/app/(pages)/(with-nav)/subjects/page.tsx +++ b/app/(pages)/(with-nav)/subjects/page.tsx @@ -23,7 +23,7 @@ const Page = async () => { teamSubjects, } = subjects.reduce( (acc, subject) => { - if (subject.team_id === user.id) { + if (subject.team_id === user.app_metadata.active_team_id) { if (subject.archived) acc.archivedTeamSubjects.push(subject); else acc.teamSubjects.push(subject); } else { @@ -48,17 +48,17 @@ const Page = async () => { {!clientSubjects.length && !teamSubjects.length && ( - {user.user_metadata.is_client ? ( + {user.app_metadata.is_client ? ( 'No active subjects.' ) : (
- Create a subject using the - yellow + Create a new subject using + the yellow
- button for eternal glory. + button. Let’s make changes.
)}
diff --git a/app/(pages)/@modal/(layout)/hey/page.tsx b/app/(pages)/@modal/(layout)/hey/page.tsx index 7bdddbd3..fd9da88b 100644 --- a/app/(pages)/@modal/(layout)/hey/page.tsx +++ b/app/(pages)/@modal/(layout)/hey/page.tsx @@ -14,24 +14,22 @@ const Page = async () => { title={`Hey, ${user.user_metadata.first_name}!`} />
-
+

At llog, our mission is to empower behavior professionals and their - clients by crafting intuitive and effective software tools that make - lasting behavior change more attainable. -

-

- Your feedback is invaluable as we evolve and improve. Whether you - have thoughts, ideas, concerns or even dreams, we’d love to - hear from you. Feel free to drop us a message at{' '} - {' '} - or{' '} - - . + clients with tools that make lasting behavior change + more attainable. Your feedback is invaluable as we evolve and + improve. Whether you have thoughts, ideas, concerns or even dreams:{' '} + + {' '} + or{' '} + + . +

Happy behavior hacking!

~ Cade, Founder

diff --git a/app/(pages)/@modal/(layout)/subjects/[subjectId]/edit/page.tsx b/app/(pages)/@modal/(layout)/subjects/[subjectId]/edit/page.tsx index 51ed3da9..5217b3d5 100644 --- a/app/(pages)/@modal/(layout)/subjects/[subjectId]/edit/page.tsx +++ b/app/(pages)/@modal/(layout)/subjects/[subjectId]/edit/page.tsx @@ -2,6 +2,7 @@ import * as Modal from '@/_components/modal'; import PageModalHeader from '@/_components/page-modal-header'; import SubjectForm from '@/_components/subject-form'; import getSubject from '@/_queries/get-subject'; +import listTags from '@/_queries/list-tags'; interface PageProps { params: Promise<{ subjectId: string }>; @@ -9,13 +10,18 @@ interface PageProps { const Page = async ({ params }: PageProps) => { const { subjectId } = await params; - const { data: subject } = await getSubject(subjectId); - if (!subject) return null; + + const [{ data: subject }, { data: tags }] = await Promise.all([ + getSubject(subjectId), + listTags(), + ]); + + if (!subject || !tags) return null; return ( - + ); }; diff --git a/app/(pages)/@modal/(layout)/subjects/[subjectId]/event-types/[eventTypeId]/page.tsx b/app/(pages)/@modal/(layout)/subjects/[subjectId]/event-types/[eventTypeId]/page.tsx index 15b7a065..bec762f8 100644 --- a/app/(pages)/@modal/(layout)/subjects/[subjectId]/event-types/[eventTypeId]/page.tsx +++ b/app/(pages)/@modal/(layout)/subjects/[subjectId]/event-types/[eventTypeId]/page.tsx @@ -20,7 +20,9 @@ const Page = async ({ params }: PageProps) => { ]); if (!subject || !eventType) return null; - const isTeamMember = !!user && subject.team_id === user.id; + + const isTeamMember = + !!user && subject.team_id === user.app_metadata.active_team_id; return ( diff --git a/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/[sessionId]/edit/page.tsx b/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/[sessionId]/edit/page.tsx index 2158a134..cd8dc27d 100644 --- a/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/[sessionId]/edit/page.tsx +++ b/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/[sessionId]/edit/page.tsx @@ -48,8 +48,7 @@ const Page = async ({ params }: PageProps) => { !subject || !subjects || !protocol || - !user || - subject.team_id !== user.id + !user ) { return null; } diff --git a/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/create/[order]/from-session/[sessionId]/page.tsx b/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/create/[order]/from-session/[sessionId]/page.tsx index 7eded957..8a7a3376 100644 --- a/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/create/[order]/from-session/[sessionId]/page.tsx +++ b/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/create/[order]/from-session/[sessionId]/page.tsx @@ -48,8 +48,7 @@ const Page = async ({ params }: PageProps) => { !subject || !subjects || !protocol || - !user || - subject.team_id !== user.id + !user ) { return null; } diff --git a/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/create/[order]/page.tsx b/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/create/[order]/page.tsx index cc7b47a2..102eb49a 100644 --- a/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/create/[order]/page.tsx +++ b/app/(pages)/@modal/(layout)/subjects/[subjectId]/protocols/[protocolId]/sessions/create/[order]/page.tsx @@ -39,8 +39,7 @@ const Page = async ({ params }: PageProps) => { !subject || !subjects || !protocol || - !user || - subject.team_id !== user.id + !user ) { return null; } diff --git a/app/(pages)/@modal/(layout)/subjects/create/page.tsx b/app/(pages)/@modal/(layout)/subjects/create/page.tsx index 2e4f75ab..30d08871 100644 --- a/app/(pages)/@modal/(layout)/subjects/create/page.tsx +++ b/app/(pages)/@modal/(layout)/subjects/create/page.tsx @@ -1,12 +1,18 @@ import * as Modal from '@/_components/modal'; import PageModalHeader from '@/_components/page-modal-header'; import SubjectForm from '@/_components/subject-form'; +import listTags from '@/_queries/list-tags'; -const Page = () => ( - - - - -); +const Page = async () => { + const { data: tags } = await listTags(); + if (!tags) return null; + + return ( + + + + + ); +}; export default Page; diff --git a/app/(pages)/@modal/(layout)/teams/[teamId]/edit/loading.tsx b/app/(pages)/@modal/(layout)/teams/[teamId]/edit/loading.tsx new file mode 100644 index 00000000..e3d948d8 --- /dev/null +++ b/app/(pages)/@modal/(layout)/teams/[teamId]/edit/loading.tsx @@ -0,0 +1,5 @@ +import PageModalLoading from '@/_components/page-modal-loading'; + +const Loading = PageModalLoading; + +export default Loading; diff --git a/app/(pages)/@modal/(layout)/teams/[teamId]/edit/page.tsx b/app/(pages)/@modal/(layout)/teams/[teamId]/edit/page.tsx new file mode 100644 index 00000000..1411c89e --- /dev/null +++ b/app/(pages)/@modal/(layout)/teams/[teamId]/edit/page.tsx @@ -0,0 +1,23 @@ +import * as Modal from '@/_components/modal'; +import PageModalHeader from '@/_components/page-modal-header'; +import TeamForm from '@/_components/team-form'; +import getTeam from '@/_queries/get-team'; + +interface PageProps { + params: Promise<{ teamId: string }>; +} + +const Page = async ({ params }: PageProps) => { + const { teamId } = await params; + const { data: team } = await getTeam(teamId); + if (!team) return null; + + return ( + + + + + ); +}; + +export default Page; diff --git a/app/(pages)/@modal/(layout)/teams/create/loading.tsx b/app/(pages)/@modal/(layout)/teams/create/loading.tsx new file mode 100644 index 00000000..e3d948d8 --- /dev/null +++ b/app/(pages)/@modal/(layout)/teams/create/loading.tsx @@ -0,0 +1,5 @@ +import PageModalLoading from '@/_components/page-modal-loading'; + +const Loading = PageModalLoading; + +export default Loading; diff --git a/app/(pages)/@modal/(layout)/teams/create/page.tsx b/app/(pages)/@modal/(layout)/teams/create/page.tsx new file mode 100644 index 00000000..82250c85 --- /dev/null +++ b/app/(pages)/@modal/(layout)/teams/create/page.tsx @@ -0,0 +1,12 @@ +import * as Modal from '@/_components/modal'; +import PageModalHeader from '@/_components/page-modal-header'; +import TeamForm from '@/_components/team-form'; + +const Page = async () => ( + + + + +); + +export default Page; diff --git a/app/(pages)/subjects/[subjectId]/join/[shareCode]/page.tsx b/app/(pages)/subjects/[subjectId]/join/[shareCode]/page.tsx index d671f515..676d26bb 100644 --- a/app/(pages)/subjects/[subjectId]/join/[shareCode]/page.tsx +++ b/app/(pages)/subjects/[subjectId]/join/[shareCode]/page.tsx @@ -13,9 +13,7 @@ const Page = async ({ params }: PageProps) => { if (!subject) { await ( await createServerSupabaseClient() - ).rpc('join_subject_as_manager', { - share_code: shareCode, - }); + ).rpc('join_subject_as_client', { share_code: shareCode }); } redirect(`/subjects/${subjectId}`); diff --git a/app/_components/account-menu.tsx b/app/_components/account-menu.tsx index 972c6904..7583da6d 100644 --- a/app/_components/account-menu.tsx +++ b/app/_components/account-menu.tsx @@ -4,31 +4,45 @@ import Avatar from '@/_components/avatar'; import Button from '@/_components/button'; import * as Drawer from '@/_components/drawer'; import SubscriptionStatus from '@/_constants/enum-subscription-status'; +import setActiveTeam from '@/_mutations/set-active-team'; import signOut from '@/_mutations/sign-out'; import getCustomerBillingPortal from '@/_queries/get-customer-billing-portal'; +import { ListTeamsData } from '@/_queries/list-teams'; import ArrowLeftStartOnRectangleIcon from '@heroicons/react/24/outline/ArrowLeftStartOnRectangleIcon'; import AtSymbolIcon from '@heroicons/react/24/outline/AtSymbolIcon'; +import CheckIcon from '@heroicons/react/24/outline/CheckIcon'; import CreditCardIcon from '@heroicons/react/24/outline/CreditCardIcon'; import HeartIcon from '@heroicons/react/24/outline/HeartIcon'; import LockClosedIcon from '@heroicons/react/24/outline/LockClosedIcon'; +import PencilIcon from '@heroicons/react/24/outline/PencilIcon'; +import PlusIcon from '@heroicons/react/24/outline/PlusIcon'; import RocketLaunchIcon from '@heroicons/react/24/outline/RocketLaunchIcon'; -import UserCircleIcon from '@heroicons/react/24/outline/UserCircleIcon'; import { User } from '@supabase/supabase-js'; import { useState, useTransition } from 'react'; interface AccountMenuProps { - user: User; + user: NonNullable; + teams: NonNullable; } -const AccountMenu = ({ user }: AccountMenuProps) => { +const AccountMenu = ({ user, teams }: AccountMenuProps) => { const [isSignOutTransitioning, startSignOutTransition] = useTransition(); + const [isChangeTeamsTransitioning, startChangeTeamsTransition] = + useTransition(); + + const [changeTeamsId, setChangeTeamsId] = useState(null); + const [isBillingRedirectLoading, setIsBillingRedirectLoading] = useState(false); const isSubscribed = user.app_metadata.subscription_status === SubscriptionStatus.Active; + const activeTeam = teams.find( + (team) => team.id === user.app_metadata.active_team_id, + ); + return ( @@ -39,8 +53,12 @@ const AccountMenu = ({ user }: AccountMenuProps) => {
Account @@ -51,53 +69,110 @@ const AccountMenu = ({ user }: AccountMenuProps) => { Account menu - - - Feedback - - + {!user.app_metadata.is_client && ( + <> + {teams.map((team) => { + const isLoading = + isChangeTeamsTransitioning && changeTeamsId === team.id; + + return ( + + { + e.preventDefault(); + setChangeTeamsId(team.id); + startChangeTeamsTransition(() => + setActiveTeam(team.id), + ); + }} + > + {!isLoading && ( + + )} + {team.name} + {((team.id === activeTeam?.id && + !isChangeTeamsTransitioning) || + isLoading) && ( + + )} + + + + + + + + + Organization menu + + + + Edit + + { + if (!isSubscribed) return; + e.preventDefault(); + setIsBillingRedirectLoading(true); + const { url } = await getCustomerBillingPortal(); + if (url) location.href = url; + else setIsBillingRedirectLoading(false); + }} + > + {isSubscribed ? ( + <> + + Manage billing + + ) : ( + <> + + Upgrade plan + + )} + + + + + + ); + })} + + + New organization + + + + )} - + Edit profile + + + Change password + Change email - - - Change password + + + + Give feedback - {!user?.user_metadata?.is_client && ( - <> - - { - if (!isSubscribed) return; - e.preventDefault(); - setIsBillingRedirectLoading(true); - const { url } = await getCustomerBillingPortal(); - if (url) location.href = url; - else setIsBillingRedirectLoading(false); - }} - > - {isSubscribed ? ( - <> - - Manage billing - - ) : ( - <> - - Upgrade plan - - )} - - - )} { }); const router = useRouter(); + const avatar = form.watch('avatar'); return (
startTransition(async () => { - const supabase = createBrowserSupabaseClient(); - - if (!values.avatar) { - await Promise.all([ - supabase.storage.from('profiles').remove([`${user.id}/avatar`]), - supabase.auth.updateUser({ data: { image_uri: null } }), - ]); - } - - if (values.avatar instanceof File) { - await supabase.storage - .from('profiles') - .upload(`${user.id}/avatar`, values.avatar, { upsert: true }); - } + await upsertAvatar({ + avatar: values.avatar, + bucket: 'profiles', + id: user.id, + }); const res = await updateUser({ first_name: values.firstName, @@ -81,13 +73,15 @@ const AccountProfileForm = ({ user }: AccountProfileFormProps) => {
- Profile image - form.setValue('avatar', null)}> - Remove image - + Image + {avatar && ( + form.setValue('avatar', null)}> + Remove image + + )} form.setValue('avatar', files[0])} /> diff --git a/app/_components/avatar-dropzone.tsx b/app/_components/avatar-dropzone.tsx index ecea1994..89283fc2 100644 --- a/app/_components/avatar-dropzone.tsx +++ b/app/_components/avatar-dropzone.tsx @@ -25,7 +25,7 @@ const AvatarDropzone = ({ return (
diff --git a/app/_components/checkbox.tsx b/app/_components/checkbox.tsx index 24cd38f0..1c8a6a87 100644 --- a/app/_components/checkbox.tsx +++ b/app/_components/checkbox.tsx @@ -11,7 +11,7 @@ const Checkbox = forwardRef( ({ className, label, name, ...rest }, ref) => (
@@ -23,6 +23,9 @@ const Checkbox = forwardRef( type="checkbox" {...rest} /> +
+ +
( > {label && }
-
), ); diff --git a/app/_components/drawer.tsx b/app/_components/drawer.tsx index 0a1b9eb4..a8762b3a 100644 --- a/app/_components/drawer.tsx +++ b/app/_components/drawer.tsx @@ -1,6 +1,8 @@ 'use client'; import ButtonPrimitive from '@/_components/button'; +import IconButtonPrimitive from '@/_components/icon-button'; +import EllipsisVerticalIcon from '@heroicons/react/24/outline/EllipsisVerticalIcon'; import { usePrevious } from '@uidotdev/usehooks'; import { usePathname } from 'next/navigation'; import * as React from 'react'; @@ -16,17 +18,31 @@ const DrawerContext = React.createContext<{ const Button = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( +>(({ children, className, ...props }, ref) => ( + > + {children} + )); Button.displayName = ButtonPrimitive.displayName; +const ButtonGroup = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); + +ButtonGroup.displayName = 'ButtonGroup'; + const Close = DrawerPrimitive.Close; const Content = React.forwardRef< @@ -36,7 +52,7 @@ const Content = React.forwardRef< , + Omit, 'icon'> +>(({ className, ...props }, ref) => ( + } + ref={ref} + variant="primary" + {...props} + /> +)); + +MoreButton.displayName = 'MoreButton'; + const NestedRoot = ( props: React.ComponentProps, ) => { @@ -157,9 +192,11 @@ Trigger.displayName = DrawerPrimitive.Trigger.displayName; export { Button, + ButtonGroup, Close, Content, Description, + MoreButton, NestedRoot, Overlay, Portal, diff --git a/app/_components/empty.tsx b/app/_components/empty.tsx index c3ecfb58..e026ad3f 100644 --- a/app/_components/empty.tsx +++ b/app/_components/empty.tsx @@ -9,7 +9,7 @@ interface EmptyProps { const Empty = ({ children, className }: EmptyProps) => (
diff --git a/app/_components/event-form.tsx b/app/_components/event-form.tsx index 1a1232e7..9a3f8f99 100644 --- a/app/_components/event-form.tsx +++ b/app/_components/event-form.tsx @@ -9,7 +9,7 @@ import InputRoot from '@/_components/input-root'; import * as Label from '@/_components/label'; import * as Modal from '@/_components/modal'; import RichTextarea from '@/_components/rich-textarea'; -import Select, { IOption } from '@/_components/select'; +import Select, { IOption } from '@/_components/select-v1'; import InputType from '@/_constants/enum-input-type'; import upsertEvent from '@/_mutations/upsert-event'; import { GetEventData } from '@/_queries/get-event'; @@ -17,8 +17,6 @@ import { GetEventTypeWithInputsAndOptionsData } from '@/_queries/get-event-type- import { GetSessionWithDetailsData } from '@/_queries/get-session-with-details'; import DurationInputType from '@/_types/duration-input'; import { InputSettingsJson } from '@/_types/input-settings-json'; -import MultiSelectInputType from '@/_types/multi-select-input-type'; -import SelectInputType from '@/_types/select-input-type'; import forceArray from '@/_utilities/force-array'; import formatDatetimeLocal from '@/_utilities/format-datetime-local'; import parseSeconds from '@/_utilities/parse-seconds'; @@ -38,7 +36,7 @@ interface EventFormProps { | NonNullable | NonNullable['modules'][0]; isArchived?: boolean; - isMission?: boolean; + isProtocol?: boolean; isPreviousModulePending?: boolean; isPublic?: boolean; subjectId: string; @@ -47,13 +45,7 @@ interface EventFormProps { export interface EventFormValues { comment: string; completionTime: string; - inputs: Array< - | DurationInputType - | MultiSelectInputType - | SelectInputType - | boolean - | string - >; + inputs: Array; } const EventForm = ({ @@ -61,7 +53,7 @@ const EventForm = ({ event, eventType, isArchived, - isMission, + isProtocol, isPreviousModulePending, isPublic, subjectId, @@ -103,17 +95,11 @@ const EventForm = ({ } case InputType.MultiSelect: { - return inputInputs.map(({ input_option_id }) => - input.options.find(({ id }) => input_option_id === id), - ); + return inputInputs.map(({ input_option_id }) => input_option_id); } case InputType.Select: { - return ( - input.options.find( - ({ id }) => id === inputInputs[0]?.input_option_id, - ) ?? null - ); + return inputInputs[0]?.input_option_id ?? null; } } }) as EventFormValues['inputs'], @@ -124,7 +110,7 @@ const EventForm = ({ const router = useRouter(); useEffect(() => { - if (!isMission || event) return; + if (!isProtocol || event) return; const interval: NodeJS.Timeout = setInterval(() => { if (form.formState.dirtyFields.completionTime) { @@ -136,7 +122,7 @@ const EventForm = ({ }, 1000); return () => clearInterval(interval); - }, [event, form, isMission]); + }, [event, form, isProtocol]); return ( - {isMission ? 'Completion time' : 'Event time'} + {isProtocol ? 'Completion time' : 'Event time'} ( - + )} /> )} @@ -313,7 +300,7 @@ const EventForm = ({ )} {!isPublic && !isArchived && (!event || form.formState.isDirty) && (
- {!event && !isMission && ( + {!event && !isProtocol && ( diff --git a/app/_components/event-page.tsx b/app/_components/event-page.tsx index 9248ce00..46196791 100644 --- a/app/_components/event-page.tsx +++ b/app/_components/event-page.tsx @@ -25,7 +25,8 @@ const EventPage = async ({ eventId, isPublic, subjectId }: EventPageProps) => { ]); if (!subject || !event || !event.type) return null; - const isTeamMember = !!user && subject.team_id === user.id; + const isTeamMember = + !!user && subject.team_id === user.app_metadata.active_team_id; const shareOrSubjects = isPublic ? 'share' : 'subjects'; return ( diff --git a/app/_components/event-select.tsx b/app/_components/event-select.tsx index 94254226..5d5a072c 100644 --- a/app/_components/event-select.tsx +++ b/app/_components/event-select.tsx @@ -3,18 +3,15 @@ import { EventFormValues } from '@/_components/event-form'; import InputRoot from '@/_components/input-root'; import * as Label from '@/_components/label'; -import Select, { IOption } from '@/_components/select'; +import Select from '@/_components/select-v2'; import createInputOption from '@/_mutations/create-input-option'; import { Database } from '@/_types/database'; import { InputSettingsJson } from '@/_types/input-settings-json'; import { useRouter } from 'next/navigation'; -import { useTransition } from 'react'; import { ControllerRenderProps } from 'react-hook-form'; -import { PropsValue } from 'react-select'; interface EventSelectProps { field: ControllerRenderProps; - id: string; input: Pick< Database['public']['Tables']['inputs']['Row'], 'id' | 'label' | 'settings' | 'type' @@ -25,63 +22,41 @@ interface EventSelectProps { }; } -const EventSelect = ({ field, id, input }: EventSelectProps) => { - const [isTransitioning, startTransition] = useTransition(); - - const optionOrOptions = - input.type === 'multi_select' ? 'options' : 'an option'; - +const EventSelect = ({ field, input }: EventSelectProps) => { const inputSettings = input.settings as InputSettingsJson; - - const placeholder = inputSettings?.isCreatable - ? `Select ${optionOrOptions} or create your own…` - : `Select ${optionOrOptions}…`; - const router = useRouter(); return ( - - {input.label} - + {input.label} { + if (e.key === 'Enter') { + e.preventDefault(); + onCreate(); + } + }} + placeholder={`Add ${optionName}…`} + ref={ref} + {...rest} + /> +
+ } + label={`Add ${optionName}`} + loading={isTransitioning} + loadingText={`Adding ${optionName}…`} + onClick={onCreate} + /> +
+
+ ); +}); + +SelectCreateOptionInput.displayName = 'CreateInput'; + +export interface Option { + id: string; + label: React.ReactNode; +} + +interface SelectProps { + isCreatable?: boolean; + isMulti?: boolean; + isReorderable?: boolean; + onChange: (value: string | string[]) => void; + onCreateOption: (value: string) => Promise; + optionName?: string; + options: Option[]; + placeholder?: string; + value: string | string[]; +} + +const Select = ({ + isCreatable, + isMulti, + isReorderable, + onChange, + onCreateOption, + optionName = 'option', + options, + placeholder, + value, +}: SelectProps) => { + const [, startTransition] = React.useTransition(); + const [filteredOptions, setFilteredOptions] = React.useState([]); + const filterRef = React.useRef(null); + const optionsLen = options.length; + const optionsMap = new Map(options.map((option) => [option.id, option])); + const prevOptionsLen = usePrevious(optionsLen); + + const toggleGroupProps = isMulti + ? { + onValueChange: onChange as (value: string[]) => void, + type: 'multiple' as const, + value: value as string[], + } + : { + onValueChange: onChange as (value: string) => void, + type: 'single' as const, + value: value as string, + }; + + const fuse = React.useMemo( + () => new Fuse(options, { keys: ['label'], threshold: 0.3 }), + [options], + ); + + const filterOptions = React.useCallback( + (value: string) => + setFilteredOptions(fuse.search(value).map((result) => result.item)), + [fuse], + ); + + const onFilter = ({ + target: { value }, + }: React.ChangeEvent) => + startTransition(() => { + if (value) filterOptions(value); + else setFilteredOptions(options); + }); + + React.useEffect(() => { + if (optionsLen !== prevOptionsLen) { + setFilteredOptions(options); + if (filterRef.current?.value) filterOptions(filterRef.current.value); + } + }, [filterOptions, options, optionsLen, prevOptionsLen]); + + if (options.length < 8 && !isReorderable) { + return ( + <> + {!!options.length && ( + + {options.map((option) => ( + + {option.label} + + ))} + + )} + {isCreatable && ( + + )} + + ); + } + + return ( + + {isMulti && !!value.length && ( + + {options + .filter((option) => value.includes(option.id)) + .map((option) => ( + + {option.label} + + ))} + + )} +
+ + {!isMulti && value ? ( + optionsMap.get(value as string)?.label + ) : ( + + {placeholder ?? + (isMulti + ? `Select ${optionName}s…` + : `Select an ${optionName}…`)} + + )} + + +
+ + + + Select ${optionName}s + + + {filteredOptions.map((option) => ( + + {option.label} + + ))} + + + + + + + +
+ ); +}; + +Select.displayName = 'Select'; + +export default Select; diff --git a/app/_components/session-page.tsx b/app/_components/session-page.tsx index 905a18fe..8ede8ed7 100644 --- a/app/_components/session-page.tsx +++ b/app/_components/session-page.tsx @@ -50,7 +50,8 @@ const SessionPage = async ({ const currentSession = protocol.sessions.find((s) => s.id === sessionId); if (!currentSession) return null; - const isTeamMember = !!user && subject.team_id === user.id; + const isTeamMember = + !!user && subject.team_id === user.app_metadata.active_team_id; const shareOrSubjects = isPublic ? 'share' : 'subjects'; let { diff --git a/app/_components/session-use-template-drawer.tsx b/app/_components/session-use-template-drawer.tsx index e90b367b..28e457b2 100644 --- a/app/_components/session-use-template-drawer.tsx +++ b/app/_components/session-use-template-drawer.tsx @@ -2,7 +2,7 @@ import Button from '@/_components/button'; import * as Drawer from '@/_components/drawer'; -import Select, { IOption } from '@/_components/select'; +import Select, { IOption } from '@/_components/select-v1'; import getTemplateData from '@/_queries/get-template-data'; import { ListInputsBySubjectIdData } from '@/_queries/list-inputs-by-subject-id'; import { ListTemplatesData } from '@/_queries/list-templates'; diff --git a/app/_components/sessions-page.tsx b/app/_components/sessions-page.tsx index 2e8da7e1..ac9087d4 100644 --- a/app/_components/sessions-page.tsx +++ b/app/_components/sessions-page.tsx @@ -34,7 +34,8 @@ const SessionsPage = async ({ ]); if (!subject) return null; - const isTeamMember = !!user && subject.team_id === user.id; + const isTeamMember = + !!user && subject.team_id === user.app_metadata.active_team_id; const { data: protocol } = isPublic ? await getPublicProtocolWithSessionsAndEvents(protocolId) diff --git a/app/_components/sign-up-form.tsx b/app/_components/sign-up-form.tsx index 73778b25..c379a39e 100644 --- a/app/_components/sign-up-form.tsx +++ b/app/_components/sign-up-form.tsx @@ -13,10 +13,18 @@ interface SignUpFormProps { const SignUpForm = ({ next }: SignUpFormProps) => { const [state, action] = useActionState(signUp.bind(null, { next }), { - defaultValues: { email: '', firstName: '', lastName: '', password: '' }, + defaultValues: { + email: '', + firstName: '', + lastName: '', + organization: '', + password: '', + }, error: '', }); + const isClient = !!next?.includes('/join/'); + return (
@@ -37,6 +45,17 @@ const SignUpForm = ({ next }: SignUpFormProps) => { />
+ {!isClient && ( + + + Organization (optional) + + + + )} Email address ; + tags: NonNullable; } export interface SubjectFormValues { avatar: File | string | null; data: SubjectDataJson; name: string; + tags: string[]; } -const SubjectForm = ({ subject }: SubjectFormProps) => { +const SubjectForm = ({ subject, tags }: SubjectFormProps) => { const [isTransitioning, startTransition] = useTransition(); const router = useRouter(); const subjectData = subject?.data as SubjectDataJson; @@ -39,6 +44,7 @@ const SubjectForm = ({ subject }: SubjectFormProps) => { avatar: subject?.image_uri, data: subjectData, name: subject?.name, + tags: subject?.tags?.map((t) => t.tag_id) ?? [], }, }); @@ -47,14 +53,24 @@ const SubjectForm = ({ subject }: SubjectFormProps) => { name: 'data.links', }); + const avatar = form.watch('avatar'); + return ( startTransition(async () => { + if (subject) { + await upsertAvatar({ + avatar: values.avatar, + bucket: 'subjects', + id: subject.id, + }); + } + const res = await upsertSubject( { subjectId: subject?.id }, - { data: values.data, name: values.name }, + { data: values.data, name: values.name, tags: values.tags }, ); if (res.error) { @@ -62,27 +78,18 @@ const SubjectForm = ({ subject }: SubjectFormProps) => { return; } - const supabase = createBrowserSupabaseClient(); - const subjectId = res.data!.id; - - if (!values.avatar) { - await Promise.all([ - supabase.storage.from('subjects').remove([`${subjectId}/avatar`]), - supabase - .from('subjects') - .update({ image_uri: null }) - .eq('id', subjectId), - ]); + if (subject) { + router.back(); + return; } - if (values.avatar instanceof File) { - await supabase.storage - .from('subjects') - .upload(`${subjectId}/avatar`, values.avatar, { upsert: true }); - } + await upsertAvatar({ + avatar: values.avatar, + bucket: 'subjects', + id: res.data!.id, + }); - if (!subject?.id) router.replace(`/subjects/${subjectId}`); - else router.back(); + router.replace(`/subjects/${res.data!.id}`); }), )} > @@ -92,15 +99,15 @@ const SubjectForm = ({ subject }: SubjectFormProps) => { - Profile image - {form.watch('avatar') && ( + Image + {avatar && ( form.setValue('avatar', null)}> Remove image )} form.setValue('avatar', files[0])} /> @@ -164,6 +171,33 @@ const SubjectForm = ({ subject }: SubjectFormProps) => {
+ + Tags + ( + + + + Image + {avatar && ( + form.setValue('avatar', null)}> + Remove image + + )} + form.setValue('avatar', files[0])} + /> + + {form.formState.errors.root && ( +
{form.formState.errors.root.message}
+ )} +
+ + + + +
+ + ); +}; + +export default AccountProfileForm; diff --git a/app/_components/template-form-section.tsx b/app/_components/template-form-section.tsx index a7da3aa0..73648bbd 100644 --- a/app/_components/template-form-section.tsx +++ b/app/_components/template-form-section.tsx @@ -3,7 +3,7 @@ import Input from '@/_components/input'; import InputRoot from '@/_components/input-root'; import * as Label from '@/_components/label'; -import Select, { IOption } from '@/_components/select'; +import Select, { IOption } from '@/_components/select-v1'; import { ListSubjectsByTeamIdData } from '@/_queries/list-subjects-by-team-id'; import * as Form from 'react-hook-form'; import { PropsValue } from 'react-select'; diff --git a/app/_components/toggle-group.tsx b/app/_components/toggle-group.tsx new file mode 100644 index 00000000..b79d9e9c --- /dev/null +++ b/app/_components/toggle-group.tsx @@ -0,0 +1,47 @@ +'use client'; + +import CheckIcon from '@heroicons/react/24/outline/CheckIcon'; +import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group'; +import * as React from 'react'; +import { twMerge } from 'tailwind-merge'; + +const Item = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ children, className, ...props }, ref) => ( + +
+ +
+ {children} +
+)); + +Item.displayName = ToggleGroupPrimitive.Item.displayName; + +const Root = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ children, className, ...props }, ref) => ( + + {children} + +)); + +Root.displayName = ToggleGroupPrimitive.Root.displayName; + +export { Item, Root }; diff --git a/app/_constants/enum-team-member-role.ts b/app/_constants/enum-team-member-role.ts new file mode 100644 index 00000000..5e72ee3e --- /dev/null +++ b/app/_constants/enum-team-member-role.ts @@ -0,0 +1,8 @@ +enum TeamMemberRole { + Admin = 'admin', + Owner = 'owner', + Recorder = 'recorder', + Viewer = 'viewer', +} + +export default TeamMemberRole; diff --git a/app/_mutations/create-tag.ts b/app/_mutations/create-tag.ts new file mode 100644 index 00000000..c9e190b1 --- /dev/null +++ b/app/_mutations/create-tag.ts @@ -0,0 +1,17 @@ +'use server'; + +import getCurrentUser from '@/_queries/get-current-user'; +import createServerSupabaseClient from '@/_utilities/create-server-supabase-client'; + +const createTag = async ({ name }: { name: string }) => { + const supabase = await createServerSupabaseClient(); + const user = await getCurrentUser(); + + return supabase + .from('tags') + .insert({ name, team_id: user?.app_metadata?.active_team_id }) + .select('id') + .single(); +}; + +export default createTag; diff --git a/app/_mutations/set-active-team.ts b/app/_mutations/set-active-team.ts new file mode 100644 index 00000000..8cf576ac --- /dev/null +++ b/app/_mutations/set-active-team.ts @@ -0,0 +1,32 @@ +'use server'; + +import getCurrentUser from '@/_queries/get-current-user'; +import createServerSupabaseClient from '@/_utilities/create-server-supabase-client'; +import { revalidatePath } from 'next/cache'; + +const setActiveTeam = async (teamId: string) => { + const supabase = await createServerSupabaseClient(); + + const { data: team } = await supabase + .from('teams') + .select('id') + .eq('id', teamId) + .single(); + + if (!team) return; + + const supabaseService = await createServerSupabaseClient({ + apiKey: process.env.SUPABASE_SERVICE_KEY!, + }); + + await supabaseService.auth.admin.updateUserById( + (await getCurrentUser())?.id ?? '', + { + app_metadata: { active_team_id: team.id }, + }, + ); + + revalidatePath('/', 'layout'); +}; + +export default setActiveTeam; diff --git a/app/_mutations/sign-up.ts b/app/_mutations/sign-up.ts index f7ecf436..27007983 100644 --- a/app/_mutations/sign-up.ts +++ b/app/_mutations/sign-up.ts @@ -4,6 +4,7 @@ import createServerSupabaseClient from '@/_utilities/create-server-supabase-clie import { headers } from 'next/headers'; import { redirect } from 'next/navigation'; import { Resend } from 'resend'; +import { v4 } from 'uuid'; const signUp = async ( context: { next?: string }, @@ -12,6 +13,7 @@ const signUp = async ( email: string; firstName: string; lastName: string; + organization: string; password: string; }; error: string; @@ -19,39 +21,52 @@ const signUp = async ( data: FormData, ) => { const { get } = await headers(); - const proto = get('x-forwarded-proto'); - const host = get('host'); const email = data.get('email') as string; - const firstName = data.get('firstName') as string; - const lastName = data.get('lastName') as string; + const firstName = (data.get('firstName') as string).trim(); + const host = get('host'); + const isClient = !!context.next?.includes('/join/'); + const lastName = (data.get('lastName') as string).trim(); + const organization = (data.get('organization') as string)?.trim(); const password = data.get('password') as string; - const isClient = context.next?.includes('/join/'); + const proto = get('x-forwarded-proto'); + const teamId = v4(); - const { error } = await ( + const { + data: { user }, + error, + } = await ( await createServerSupabaseClient() ).auth.signUp({ email, options: { data: { first_name: firstName, - is_client: isClient, last_name: lastName, + organization, + team_id: teamId, }, emailRedirectTo: `${proto}://${host}${context.next ? context.next : isClient ? '/subjects' : '/hey'}`, }, password, }); - if (error) { + if (error || !user) { return { - defaultValues: { email, firstName, lastName, password }, - error: error.message, + defaultValues: { email, firstName, lastName, organization, password }, + error: error?.message ?? 'An error occurred', }; } - const resend = new Resend(process.env.RESEND_API_KEY); + await ( + await createServerSupabaseClient({ + apiKey: process.env.SUPABASE_SERVICE_KEY!, + }) + ).auth.admin.updateUserById(user.id, { + app_metadata: { active_team_id: teamId, is_client: isClient }, + user_metadata: { organization: null, team_id: null }, + }); - await resend.emails.send({ + await new Resend(process.env.RESEND_API_KEY).emails.send({ from: 'system@llog.app', html: `
${JSON.stringify(
       {
@@ -59,6 +74,7 @@ const signUp = async (
         firstName,
         isClient,
         lastName,
+        organization,
       },
       null,
       2,
diff --git a/app/_mutations/update-subject.ts b/app/_mutations/update-subject.ts
index 2d253d95..c0eca4e2 100644
--- a/app/_mutations/update-subject.ts
+++ b/app/_mutations/update-subject.ts
@@ -8,8 +8,6 @@ const updateSubject = async (
   subject: Database['public']['Tables']['subjects']['Update'] & { id: string },
 ) => {
   const supabase = await createServerSupabaseClient();
-
-  // get the latest app_metadata for rls validation
   await supabase.auth.refreshSession();
 
   await supabase
@@ -21,6 +19,7 @@ const updateSubject = async (
       public: subject.public,
     })
     .eq('id', subject.id);
+
   revalidatePath('/', 'layout');
 };
 
diff --git a/app/_mutations/upsert-avatar.ts b/app/_mutations/upsert-avatar.ts
new file mode 100644
index 00000000..6fd2ef73
--- /dev/null
+++ b/app/_mutations/upsert-avatar.ts
@@ -0,0 +1,25 @@
+import createBrowserSupabaseClient from '@/_utilities/create-browser-supabase-client';
+
+const upsertAvatar = async ({
+  avatar,
+  bucket,
+  id,
+}: {
+  avatar: File | string | null;
+  bucket: string;
+  id: string;
+}) => {
+  const supabase = createBrowserSupabaseClient();
+
+  if (!avatar) {
+    await supabase.storage.from(bucket).remove([`${id}/avatar`]);
+  }
+
+  if (avatar instanceof File) {
+    await supabase.storage
+      .from(bucket)
+      .upload(`${id}/avatar`, avatar, { upsert: true });
+  }
+};
+
+export default upsertAvatar;
diff --git a/app/_mutations/upsert-event-type-template.ts b/app/_mutations/upsert-event-type-template.ts
index 8baf924e..0ebafb23 100644
--- a/app/_mutations/upsert-event-type-template.ts
+++ b/app/_mutations/upsert-event-type-template.ts
@@ -2,6 +2,7 @@
 
 import { EventTypeTemplateFormValues } from '@/_components/event-type-template-form';
 import TemplateType from '@/_constants/enum-template-type';
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 import sanitizeHtml from '@/_utilities/sanitize-html';
 import { revalidatePath } from 'next/cache';
@@ -11,6 +12,7 @@ const upsertEventTypeTemplate = async (
   data: EventTypeTemplateFormValues,
 ) => {
   const supabase = await createServerSupabaseClient();
+  const user = await getCurrentUser();
 
   const { data: template, error } = await supabase
     .from('templates')
@@ -23,6 +25,7 @@ const upsertEventTypeTemplate = async (
       id: context.templateId,
       name: data.name.trim(),
       public: false,
+      team_id: user?.app_metadata?.active_team_id,
       type: TemplateType.EventType,
     })
     .select('id')
diff --git a/app/_mutations/upsert-event.ts b/app/_mutations/upsert-event.ts
index 87a30ecf..cb79f51d 100644
--- a/app/_mutations/upsert-event.ts
+++ b/app/_mutations/upsert-event.ts
@@ -5,8 +5,6 @@ import InputType from '@/_constants/enum-input-type';
 import { GetEventTypeWithInputsAndOptionsData } from '@/_queries/get-event-type-with-inputs-and-options';
 import { Database, Json } from '@/_types/database';
 import DurationInputType from '@/_types/duration-input';
-import MultiSelectInputType from '@/_types/multi-select-input-type';
-import SelectInputType from '@/_types/select-input-type';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 import sanitizeHtml from '@/_utilities/sanitize-html';
 import { revalidatePath } from 'next/cache';
@@ -16,7 +14,7 @@ const upsertEvent = async (
     eventId?: string;
     eventTypeId: string;
     eventTypeInputs: NonNullable['inputs'];
-    isMission: boolean;
+    isProtocol: boolean;
     subjectId: string;
   },
   data: EventFormValues,
@@ -48,8 +46,9 @@ const upsertEvent = async (
   }>(
     (acc, input, i) => {
       if (
-        input === '' ||
+        input === undefined ||
         input === null ||
+        input === '' ||
         (Array.isArray(input) && !input.some((v) => v))
       ) {
         return acc;
@@ -88,7 +87,7 @@ const upsertEvent = async (
         }
 
         case InputType.MultiSelect: {
-          (input as MultiSelectInputType).forEach(({ id }, order) =>
+          (input as string[]).forEach((id, order) =>
             acc.eventInputs.push({ ...payload, input_option_id: id, order }),
           );
 
@@ -96,7 +95,7 @@ const upsertEvent = async (
         }
 
         case InputType.Select: {
-          payload.input_option_id = (input as SelectInputType)?.id;
+          payload.input_option_id = input as string;
           acc.eventInputs.push(payload);
           return acc;
         }
diff --git a/app/_mutations/upsert-input.ts b/app/_mutations/upsert-input.ts
index afa14c8e..06f8a70f 100644
--- a/app/_mutations/upsert-input.ts
+++ b/app/_mutations/upsert-input.ts
@@ -2,6 +2,7 @@
 
 import { InputFormValues } from '@/_components/input-form';
 import InputType from '@/_constants/enum-input-type';
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 import { revalidatePath } from 'next/cache';
 
@@ -12,6 +13,7 @@ const upsertInput = async (
   data: InputFormValues,
 ): Promise => {
   const supabase = await createServerSupabaseClient();
+  const user = await getCurrentUser();
   const type = data.type.id;
 
   const { data: input, error } = await supabase
@@ -20,6 +22,7 @@ const upsertInput = async (
       id: context.inputId,
       label: data.label.trim(),
       settings: data.settings,
+      team_id: user?.app_metadata?.active_team_id,
       type,
     })
     .select('id, label')
diff --git a/app/_mutations/upsert-module-template.ts b/app/_mutations/upsert-module-template.ts
index b4392934..061a7cc9 100644
--- a/app/_mutations/upsert-module-template.ts
+++ b/app/_mutations/upsert-module-template.ts
@@ -2,6 +2,7 @@
 
 import { ModuleTemplateFormValues } from '@/_components/module-template-form';
 import TemplateType from '@/_constants/enum-template-type';
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 import sanitizeHtml from '@/_utilities/sanitize-html';
 import { revalidatePath } from 'next/cache';
@@ -13,6 +14,7 @@ const upsertModuleTemplate = async (
   data: ModuleTemplateFormValues,
 ): Promise => {
   const supabase = await createServerSupabaseClient();
+  const user = await getCurrentUser();
 
   const { data: template, error } = await supabase
     .from('templates')
@@ -25,6 +27,7 @@ const upsertModuleTemplate = async (
       id: context.templateId,
       name: data.name.trim(),
       public: false,
+      team_id: user?.app_metadata?.active_team_id,
       type: TemplateType.Module,
     })
     .select('id')
diff --git a/app/_mutations/upsert-protocol-template.ts b/app/_mutations/upsert-protocol-template.ts
index 391af4a8..7a25c1fc 100644
--- a/app/_mutations/upsert-protocol-template.ts
+++ b/app/_mutations/upsert-protocol-template.ts
@@ -2,6 +2,7 @@
 
 import { ProtocolTemplateFormValues } from '@/_components/protocol-template-form';
 import TemplateType from '@/_constants/enum-template-type';
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 import sanitizeHtml from '@/_utilities/sanitize-html';
 import { revalidatePath } from 'next/cache';
@@ -11,6 +12,7 @@ const upsertProtocolTemplate = async (
   data: ProtocolTemplateFormValues,
 ) => {
   const supabase = await createServerSupabaseClient();
+  const user = await getCurrentUser();
 
   const { data: template, error } = await supabase
     .from('templates')
@@ -29,6 +31,7 @@ const upsertProtocolTemplate = async (
       id: context.templateId,
       name: data.name.trim(),
       public: false,
+      team_id: user?.app_metadata?.active_team_id,
       type: TemplateType.Protocol,
     })
     .select('id')
diff --git a/app/_mutations/upsert-session-template.ts b/app/_mutations/upsert-session-template.ts
index 60474d2d..2e695f55 100644
--- a/app/_mutations/upsert-session-template.ts
+++ b/app/_mutations/upsert-session-template.ts
@@ -2,6 +2,7 @@
 
 import { SessionTemplateFormValues } from '@/_components/session-template-form';
 import TemplateType from '@/_constants/enum-template-type';
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 import sanitizeHtml from '@/_utilities/sanitize-html';
 import { revalidatePath } from 'next/cache';
@@ -11,6 +12,7 @@ const upsertSessionTemplate = async (
   data: SessionTemplateFormValues,
 ) => {
   const supabase = await createServerSupabaseClient();
+  const user = await getCurrentUser();
 
   const { data: template, error } = await supabase
     .from('templates')
@@ -26,6 +28,7 @@ const upsertSessionTemplate = async (
       id: context.templateId,
       name: data.name.trim(),
       public: false,
+      team_id: user?.app_metadata?.active_team_id,
       type: TemplateType.Session,
     })
     .select('id')
diff --git a/app/_mutations/upsert-subject.ts b/app/_mutations/upsert-subject.ts
index 2e4bf95a..a2186fae 100644
--- a/app/_mutations/upsert-subject.ts
+++ b/app/_mutations/upsert-subject.ts
@@ -1,6 +1,7 @@
 'use server';
 
 import { SubjectFormValues } from '@/_components/subject-form';
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 import sanitizeHtml from '@/_utilities/sanitize-html';
 import { revalidatePath } from 'next/cache';
@@ -10,9 +11,8 @@ const upsertSubject = async (
   data: Omit,
 ) => {
   const supabase = await createServerSupabaseClient();
-
-  // get the latest app_metadata for rls validation
   await supabase.auth.refreshSession();
+  const user = await getCurrentUser();
 
   const { data: subject, error } = await supabase
     .from('subjects')
@@ -26,11 +26,23 @@ const upsertSubject = async (
       },
       id: context.subjectId,
       name: data.name.trim(),
+      team_id: user?.app_metadata?.active_team_id,
     })
     .select('id')
     .single();
 
   if (error) return { error: error.message };
+
+  await supabase
+    .from('subject_tags')
+    .delete()
+    .eq('subject_id', subject.id)
+    .not('tag_id', 'in', `(${data.tags.join(',')})`);
+
+  await supabase
+    .from('subject_tags')
+    .upsert(data.tags.map((tag) => ({ subject_id: subject.id, tag_id: tag })));
+
   revalidatePath('/', 'layout');
   return { data: subject };
 };
diff --git a/app/_mutations/upsert-team.ts b/app/_mutations/upsert-team.ts
new file mode 100644
index 00000000..12b9ec3e
--- /dev/null
+++ b/app/_mutations/upsert-team.ts
@@ -0,0 +1,23 @@
+'use server';
+
+import { TeamFormValues } from '@/_components/team-form';
+import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
+import { revalidatePath } from 'next/cache';
+
+const upsertTeam = async (
+  context: { teamId?: string },
+  data: Omit,
+) => {
+  const res = await (
+    await createServerSupabaseClient()
+  ).rpc('upsert_team', {
+    _id: context.teamId,
+    _name: data.name,
+  });
+
+  if (res.error) return { error: res.error.message };
+  if (context.teamId) revalidatePath('/', 'layout');
+  return { data: { id: res.data } };
+};
+
+export default upsertTeam;
diff --git a/app/_queries/count-notifications.ts b/app/_queries/count-notifications.ts
index 9e29f9cb..c3179d4e 100644
--- a/app/_queries/count-notifications.ts
+++ b/app/_queries/count-notifications.ts
@@ -1,8 +1,10 @@
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 
 const countNotifications = async () =>
   (await createServerSupabaseClient())
     .from('notifications')
-    .select('*', { count: 'estimated', head: true });
+    .select('*', { count: 'estimated', head: true })
+    .eq('profile_id', (await getCurrentUser())?.id ?? '');
 
 export default countNotifications;
diff --git a/app/_queries/get-input-with-uses.ts b/app/_queries/get-input-with-uses.ts
index ea6291e5..39560817 100644
--- a/app/_queries/get-input-with-uses.ts
+++ b/app/_queries/get-input-with-uses.ts
@@ -9,7 +9,7 @@ const getInputWithUses = async (inputId: string) =>
       label,
       options:input_options(id, label),
       settings,
-      subjects(id),
+      subjects!input_subjects(id),
       type,
       uses:event_types(subject:subjects(id, name, image_uri))`,
     )
diff --git a/app/_queries/get-input.ts b/app/_queries/get-input.ts
index 1a8b12ae..9e4b4cb2 100644
--- a/app/_queries/get-input.ts
+++ b/app/_queries/get-input.ts
@@ -9,7 +9,7 @@ const getInput = async (inputId: string) =>
       label,
       options:input_options(id, label),
       settings,
-      subjects(id),
+      subjects!input_subjects(id),
       type`,
     )
     .eq('id', inputId)
diff --git a/app/_queries/get-subject.ts b/app/_queries/get-subject.ts
index 68de6905..1103ae03 100644
--- a/app/_queries/get-subject.ts
+++ b/app/_queries/get-subject.ts
@@ -3,7 +3,18 @@ import createServerSupabaseClient from '@/_utilities/create-server-supabase-clie
 const getSubject = async (subjectId: string) =>
   (await createServerSupabaseClient())
     .from('subjects')
-    .select('archived, data, id, image_uri, name, public, share_code, team_id')
+    .select(
+      `
+      archived,
+      data,
+      id,
+      image_uri,
+      name,
+      public,
+      share_code,
+      tags:subject_tags(tag_id),
+      team_id`,
+    )
     .eq('id', subjectId)
     .eq('deleted', false)
     .single();
diff --git a/app/_queries/get-team.ts b/app/_queries/get-team.ts
new file mode 100644
index 00000000..c06db6da
--- /dev/null
+++ b/app/_queries/get-team.ts
@@ -0,0 +1,12 @@
+import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
+
+const getTeam = async (teamId: string) =>
+  (await createServerSupabaseClient())
+    .from('teams')
+    .select('id, image_uri, name')
+    .eq('id', teamId)
+    .single();
+
+export type GetTeamData = Awaited>['data'];
+
+export default getTeam;
diff --git a/app/_queries/get-template-data.ts b/app/_queries/get-template-data.ts
index 2837c1d3..e42468e2 100644
--- a/app/_queries/get-template-data.ts
+++ b/app/_queries/get-template-data.ts
@@ -1,6 +1,6 @@
 import createBrowserSupabaseClient from '@/_utilities/create-browser-supabase-client';
 
-const getTemplateData = (templateId: string) =>
+const getTemplateData = async (templateId: string) =>
   createBrowserSupabaseClient()
     .from('templates')
     .select('data')
diff --git a/app/_queries/get-template.ts b/app/_queries/get-template.ts
index b1f97d70..645a207c 100644
--- a/app/_queries/get-template.ts
+++ b/app/_queries/get-template.ts
@@ -3,7 +3,7 @@ import createServerSupabaseClient from '@/_utilities/create-server-supabase-clie
 const getTemplate = async (templateId: string) =>
   (await createServerSupabaseClient())
     .from('templates')
-    .select('data, description, id, name, subjects(id), type')
+    .select('data, description, id, name, subjects!template_subjects(id), type')
     .eq('id', templateId)
     .single();
 
diff --git a/app/_queries/list-inputs-by-subject-id.ts b/app/_queries/list-inputs-by-subject-id.ts
index 268545a2..049d7e27 100644
--- a/app/_queries/list-inputs-by-subject-id.ts
+++ b/app/_queries/list-inputs-by-subject-id.ts
@@ -21,8 +21,8 @@ const listInputsBySubjectId = async (subjectId: string) => {
 
   return supabase
     .from('inputs')
-    .select('id, label, subjects(id, image_uri, name), type')
-    .eq('team_id', (await getCurrentUser())?.id ?? '')
+    .select('id, label, subjects!input_subjects(id, image_uri, name), type')
+    .eq('team_id', (await getCurrentUser())?.app_metadata?.active_team_id ?? '')
     .eq('archived', false)
     .not('id', 'in', `(${blacklist.data.map((is) => is.input_id).join(',')})`)
     .eq('subjects.deleted', false)
diff --git a/app/_queries/list-inputs-with-uses.ts b/app/_queries/list-inputs-with-uses.ts
index 7048d34e..dc0254cc 100644
--- a/app/_queries/list-inputs-with-uses.ts
+++ b/app/_queries/list-inputs-with-uses.ts
@@ -8,11 +8,11 @@ const listInputsWithUses = async () =>
       `
       id,
       label,
-      subjects(id, image_uri, name),
+      subjects!input_subjects(id, image_uri, name),
       type,
       uses:event_types(subject:subjects(id, name))`,
     )
-    .eq('team_id', (await getCurrentUser())?.id ?? '')
+    .eq('team_id', (await getCurrentUser())?.app_metadata?.active_team_id ?? '')
     .eq('archived', false)
     .eq('subjects.deleted', false)
     .not('subjects.archived', 'is', null)
diff --git a/app/_queries/list-inputs.ts b/app/_queries/list-inputs.ts
index 80af29ed..ce0a92da 100644
--- a/app/_queries/list-inputs.ts
+++ b/app/_queries/list-inputs.ts
@@ -4,8 +4,8 @@ import createServerSupabaseClient from '@/_utilities/create-server-supabase-clie
 const listInputs = async () =>
   (await createServerSupabaseClient())
     .from('inputs')
-    .select('id, label, subjects(id, image_uri, name), type')
-    .eq('team_id', (await getCurrentUser())?.id ?? '')
+    .select('id, label, subjects!input_subjects(id, image_uri, name), type')
+    .eq('team_id', (await getCurrentUser())?.app_metadata?.active_team_id ?? '')
     .eq('archived', false)
     .eq('subjects.deleted', false)
     .not('subjects.archived', 'is', null)
diff --git a/app/_queries/list-notifications.ts b/app/_queries/list-notifications.ts
index d5ac63c5..042c6877 100644
--- a/app/_queries/list-notifications.ts
+++ b/app/_queries/list-notifications.ts
@@ -1,3 +1,4 @@
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 
 const listNotifications = async () =>
@@ -38,6 +39,7 @@ const listNotifications = async () =>
       subject:subjects(id, image_uri, name),
       type`,
     )
+    .eq('profile_id', (await getCurrentUser())?.id ?? '')
     .order('created_at', { ascending: false })
     .limit(50);
 
diff --git a/app/_queries/list-subject-managers.ts b/app/_queries/list-subject-managers.ts
deleted file mode 100644
index cb596491..00000000
--- a/app/_queries/list-subject-managers.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
-
-const listSubjectManagers = async (subjectId: string) =>
-  (await createServerSupabaseClient())
-    .from('subject_managers')
-    .select('manager:profiles(id, image_uri, first_name)')
-    .eq('subject_id', subjectId)
-    .neq('profile_id', '70045ed0-b03c-46d8-a784-e05c15a770af');
-
-export type ListSubjectManagersData = Awaited<
-  ReturnType
->['data'];
-
-export default listSubjectManagers;
diff --git a/app/_queries/list-subjects-by-team-id.ts b/app/_queries/list-subjects-by-team-id.ts
index c942cbc4..0907d9f8 100644
--- a/app/_queries/list-subjects-by-team-id.ts
+++ b/app/_queries/list-subjects-by-team-id.ts
@@ -5,7 +5,7 @@ const listSubjectsByTeamId = async () =>
   (await createServerSupabaseClient())
     .from('subjects')
     .select('archived, id, image_uri, name, team_id')
-    .eq('team_id', (await getCurrentUser())?.id ?? '')
+    .eq('team_id', (await getCurrentUser())?.app_metadata?.active_team_id ?? '')
     .eq('deleted', false)
     .not('archived', 'is', null)
     .order('name');
diff --git a/app/_queries/list-subjects.ts b/app/_queries/list-subjects.ts
index ff075eb8..485c6988 100644
--- a/app/_queries/list-subjects.ts
+++ b/app/_queries/list-subjects.ts
@@ -1,13 +1,30 @@
+'use server';
+
+import getCurrentUser from '@/_queries/get-current-user';
 import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
 
-const listSubjects = async () =>
-  (await createServerSupabaseClient())
+const listSubjects = async () => {
+  const supabase = await createServerSupabaseClient();
+  const user = await getCurrentUser();
+  const userId = user?.id ?? '';
+
+  const { data: clientSubjectIds } = await supabase
+    .from('subject_clients')
+    .select('subject_id')
+    .eq('profile_id', userId);
+
+  const formattedIds = (clientSubjectIds ?? [])
+    .map((subjectClient) => subjectClient.subject_id)
+    .join(',');
+
+  return supabase
     .from('subjects')
     .select('archived, id, image_uri, name, public, share_code, team_id')
-    .not('team_id', 'is', null)
-    .eq('deleted', false)
-    .not('archived', 'is', null)
-    .order('name');
+    .or(
+      `team_id.eq.${user?.app_metadata.active_team_id}, id.in.(${formattedIds})`,
+    )
+    .eq('deleted', false);
+};
 
 export type ListSubjectsData = Awaited>['data'];
 
diff --git a/app/_queries/list-tags.ts b/app/_queries/list-tags.ts
new file mode 100644
index 00000000..54fdaf0d
--- /dev/null
+++ b/app/_queries/list-tags.ts
@@ -0,0 +1,13 @@
+import getCurrentUser from '@/_queries/get-current-user';
+import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
+
+const listTags = async () =>
+  (await createServerSupabaseClient())
+    .from('tags')
+    .select('id, name')
+    .eq('team_id', (await getCurrentUser())?.app_metadata?.active_team_id ?? '')
+    .order('name');
+
+export type ListTagsData = Awaited>['data'];
+
+export default listTags;
diff --git a/app/_queries/list-teams.ts b/app/_queries/list-teams.ts
new file mode 100644
index 00000000..5d957670
--- /dev/null
+++ b/app/_queries/list-teams.ts
@@ -0,0 +1,25 @@
+import getCurrentUser from '@/_queries/get-current-user';
+import createServerSupabaseClient from '@/_utilities/create-server-supabase-client';
+
+const listTeams = async () => {
+  const supabase = await createServerSupabaseClient();
+  const user = await getCurrentUser();
+
+  const { data: teamIds } = await supabase
+    .from('team_members')
+    .select('team_id')
+    .eq('profile_id', user?.id ?? '');
+
+  return supabase
+    .from('teams')
+    .select('id, image_uri, name')
+    .in(
+      'id',
+      (teamIds ?? []).map((teamMember) => teamMember.team_id),
+    )
+    .order('name');
+};
+
+export type ListTeamsData = Awaited>['data'];
+
+export default listTeams;
diff --git a/app/_queries/list-templates-by-subject-id-and-type.ts b/app/_queries/list-templates-by-subject-id-and-type.ts
index d92e2e86..ea3bcf3d 100644
--- a/app/_queries/list-templates-by-subject-id-and-type.ts
+++ b/app/_queries/list-templates-by-subject-id-and-type.ts
@@ -28,8 +28,8 @@ const listTemplatesBySubjectIdAndType = async ({
 
   return supabase
     .from('templates')
-    .select('id, name, subjects(id, image_uri, name), type')
-    .eq('team_id', (await getCurrentUser())?.id ?? '')
+    .select('id, name, subjects!template_subjects(id, image_uri, name), type')
+    .eq('team_id', (await getCurrentUser())?.app_metadata?.active_team_id ?? '')
     .eq('type', type)
     .not(
       'id',
diff --git a/app/_queries/list-templates.ts b/app/_queries/list-templates.ts
index 6258a8d0..dbd7c0d3 100644
--- a/app/_queries/list-templates.ts
+++ b/app/_queries/list-templates.ts
@@ -5,8 +5,11 @@ import createServerSupabaseClient from '@/_utilities/create-server-supabase-clie
 const listTemplates = async ({ type }: { type?: TemplateType } = {}) => {
   const q = (await createServerSupabaseClient())
     .from('templates')
-    .select('id, name, subjects(id, image_uri, name), type')
-    .eq('team_id', (await getCurrentUser())?.id ?? '');
+    .select('id, name, subjects!template_subjects(id, image_uri, name), type')
+    .eq(
+      'team_id',
+      (await getCurrentUser())?.app_metadata?.active_team_id ?? '',
+    );
 
   if (type) q.eq('type', type);
   else q.not('type', 'is', null);
diff --git a/app/_types/multi-select-input-type.ts b/app/_types/multi-select-input-type.ts
deleted file mode 100644
index 8c4db243..00000000
--- a/app/_types/multi-select-input-type.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-type MultiSelectInputType = Array<{ id: string; label: string }>;
-
-export default MultiSelectInputType;
diff --git a/app/_types/select-input-type.ts b/app/_types/select-input-type.ts
deleted file mode 100644
index bcf6a532..00000000
--- a/app/_types/select-input-type.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-type SelectInputType = { id: string; label: string } | null;
-
-export default SelectInputType;
diff --git a/app/_utilities/get-insight-options-from-events.ts b/app/_utilities/get-insight-options-from-events.ts
index d3824c5f..e9f86c5d 100644
--- a/app/_utilities/get-insight-options-from-events.ts
+++ b/app/_utilities/get-insight-options-from-events.ts
@@ -1,4 +1,4 @@
-import { IOption } from '@/_components/select';
+import { IOption } from '@/_components/select-v1';
 import InputType from '@/_constants/enum-input-type';
 import { ListEventsData } from '@/_queries/list-events';
 import firstIfArray from '@/_utilities/first-if-array';
diff --git a/bun.lockb b/bun.lockb
index 5163ca269682e0463813083c1aa7f6c33b6fb8bb..b319e99500a9a0f3f395a93607ce61bfafab6a7d 100755
GIT binary patch
delta 84133
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

delta 88337
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

diff --git a/package.json b/package.json
index 6b7f1b78..89c3bffa 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,7 @@
     "format": "prettier --write .",
     "lint": "next lint",
     "start": "next build && next start",
-    "up": "bun update --latest && bun i eslint@8.57.0 next@canary react@canary react-dom@canary"
+    "up": "bun update --latest && bun i eslint@8.57.0 react@rc react-dom@rc"
   },
   "dependencies": {
     "@dnd-kit/core": "^6.1.0",
@@ -27,60 +27,64 @@
     "@radix-ui/react-dropdown-menu": "^2.1.2",
     "@radix-ui/react-label": "^2.1.0",
     "@radix-ui/react-popover": "^1.1.2",
+    "@radix-ui/react-select": "^2.1.2",
     "@radix-ui/react-slot": "^1.1.0",
     "@radix-ui/react-switch": "^1.1.1",
+    "@radix-ui/react-toggle-group": "^1.1.0",
     "@supabase/ssr": "^0.5.1",
-    "@supabase/supabase-js": "^2.45.4",
+    "@supabase/supabase-js": "^2.45.6",
     "@tailwindcss/typography": "^0.5.15",
-    "@tiptap/extension-bold": "^2.8.0",
-    "@tiptap/extension-bullet-list": "^2.8.0",
-    "@tiptap/extension-document": "^2.8.0",
-    "@tiptap/extension-history": "^2.8.0",
-    "@tiptap/extension-italic": "^2.8.0",
-    "@tiptap/extension-link": "^2.8.0",
-    "@tiptap/extension-list-item": "^2.8.0",
-    "@tiptap/extension-ordered-list": "^2.8.0",
-    "@tiptap/extension-paragraph": "^2.8.0",
-    "@tiptap/extension-placeholder": "^2.8.0",
-    "@tiptap/extension-text": "^2.8.0",
-    "@tiptap/extension-typography": "^2.8.0",
-    "@tiptap/extension-underline": "^2.8.0",
-    "@tiptap/extension-youtube": "^2.8.0",
-    "@tiptap/react": "^2.8.0",
+    "@tiptap/extension-bold": "^2.9.1",
+    "@tiptap/extension-bullet-list": "^2.9.1",
+    "@tiptap/extension-document": "^2.9.1",
+    "@tiptap/extension-history": "^2.9.1",
+    "@tiptap/extension-italic": "^2.9.1",
+    "@tiptap/extension-link": "^2.9.1",
+    "@tiptap/extension-list-item": "^2.9.1",
+    "@tiptap/extension-ordered-list": "^2.9.1",
+    "@tiptap/extension-paragraph": "^2.9.1",
+    "@tiptap/extension-placeholder": "^2.9.1",
+    "@tiptap/extension-text": "^2.9.1",
+    "@tiptap/extension-typography": "^2.9.1",
+    "@tiptap/extension-underline": "^2.9.1",
+    "@tiptap/extension-youtube": "^2.9.1",
+    "@tiptap/react": "^2.9.1",
     "@types/humanize-duration": "^3.27.4",
-    "@types/lodash": "^4.17.10",
-    "@types/react": "^18.3.11",
-    "@types/react-dom": "^18.3.0",
-    "@typescript-eslint/eslint-plugin": "^8.8.1",
-    "@typescript-eslint/parser": "^8.8.1",
+    "@types/lodash": "^4.17.12",
+    "@types/react": "^18.3.12",
+    "@types/react-dom": "^18.3.1",
+    "@types/uuid": "^10.0.0",
+    "@typescript-eslint/eslint-plugin": "^8.11.0",
+    "@typescript-eslint/parser": "^8.11.0",
     "@uidotdev/usehooks": "^2.4.1",
-    "@vercel/analytics": "^1.3.1",
+    "@vercel/analytics": "^1.3.2",
     "@visx/responsive": "^3.10.2",
     "autoprefixer": "^10.4.20",
     "eslint": "8.57.0",
-    "eslint-config-next": "^14.2.15",
+    "eslint-config-next": "^15.0.1",
     "eslint-config-prettier": "^9.1.0",
     "fuse.js": "^7.0.0",
     "humanize-duration": "^3.32.1",
     "json-2-csv": "^5.5.6",
     "lodash": "^4.17.21",
     "nanoid": "^5.0.7",
-    "next": "^15.0.0-canary.179",
+    "next": "^15.0.1",
     "prettier": "^3.3.3",
     "prettier-plugin-organize-imports": "^4.1.0",
     "prettier-plugin-tailwindcss": "^0.6.8",
-    "react": "^19.0.0-rc-ed966dac-20241007",
-    "react-dom": "^19.0.0-rc-ed966dac-20241007",
-    "react-dropzone": "^14.2.9",
-    "react-hook-form": "7.53.0",
-    "react-select": "^5.8.1",
+    "react": "^19.0.0-rc-cae764ce-20241025",
+    "react-dom": "^19.0.0-rc-cae764ce-20241025",
+    "react-dropzone": "^14.2.10",
+    "react-hook-form": "7.53.1",
+    "react-select": "^5.8.2",
     "resend": "^4.0.0",
-    "supabase": "^1.203.0",
-    "tailwind-merge": "^2.5.3",
-    "tailwindcss": "^3.4.13",
+    "supabase": "^1.207.9",
+    "tailwind-merge": "^2.5.4",
+    "tailwindcss": "^3.4.14",
     "typescript": "^5.6.3",
-    "vaul": "^1.0.0",
-    "vercel": "^37.6.3",
+    "uuid": "^11.0.1",
+    "vaul": "^1.1.0",
+    "vercel": "^37.12.1",
     "xss": "^1.0.15"
   }
 }
diff --git a/supabase/migrations/20241011171810_add-team-functionality.sql b/supabase/migrations/20241011171810_add-team-functionality.sql
new file mode 100644
index 00000000..cc399532
--- /dev/null
+++ b/supabase/migrations/20241011171810_add-team-functionality.sql
@@ -0,0 +1,1260 @@
+-- add enums
+
+create type team_member_role as enum ('owner', 'admin', 'recorder', 'viewer');
+
+-- add tags
+
+create table "public"."input_tags" (
+  "input_id" uuid not null,
+  "tag_id" uuid not null
+);
+
+create table "public"."subject_tags" (
+  "subject_id" uuid not null,
+  "tag_id" uuid not null
+);
+
+create table "public"."tags" (
+  "id" uuid not null default gen_random_uuid (),
+  "name" text not null,
+  "team_id" uuid not null
+);
+
+create table "public"."team_member_subject_roles" (
+  "team_id" uuid not null,
+  "profile_id" uuid not null,
+  "subject_id" uuid not null,
+  "role" team_member_role not null
+);
+
+create table "public"."team_member_tag_roles" (
+  "team_id" uuid not null,
+  "profile_id" uuid not null,
+  "tag_id" uuid not null,
+  "role" team_member_role not null
+);
+
+create table "public"."template_tags" (
+  "template_id" uuid not null,
+  "tag_id" uuid not null
+);
+
+alter table "public"."input_tags" enable row level security;
+alter table "public"."subject_tags" enable row level security;
+alter table "public"."tags" enable row level security;
+alter table "public"."team_member_subject_roles" enable row level security;
+alter table "public"."team_member_tag_roles" enable row level security;
+alter table "public"."template_tags" enable row level security;
+
+create unique index input_tags_pkey on public.input_tags using btree (input_id, tag_id);
+create unique index subject_tags_pkey on public.subject_tags using btree (subject_id, tag_id);
+create unique index tags_pkey on public.tags using btree (id);
+create unique index team_member_subject_roles_pkey on public.team_member_subject_roles using btree (team_id, profile_id, subject_id);
+create unique index team_member_tag_roles_pkey on public.team_member_tag_roles using btree (team_id, profile_id, tag_id);
+create unique index template_tags_pkey on public.template_tags using btree (template_id, tag_id);
+
+create index tags_team_id_name_index on public.tags using btree (team_id, name);
+
+alter table "public"."input_tags" add constraint "input_tags_input_id_fkey" foreign key (input_id) references inputs (id) on update cascade on delete cascade not valid;
+alter table "public"."input_tags" add constraint "input_tags_pkey" primary key using index "input_tags_pkey";
+alter table "public"."input_tags" add constraint "input_tags_tag_id_fkey" foreign key (tag_id) references tags (id) on update cascade on delete cascade not valid;
+alter table "public"."input_tags" add constraint "input_tags_template_id_fkey" foreign key (input_id) references subjects (id) on update cascade on delete cascade not valid;
+alter table "public"."input_tags" add constraint "input_tags_template_id_fkey1" foreign key (input_id) references templates (id) on update cascade on delete cascade not valid;
+alter table "public"."input_tags" validate constraint "input_tags_input_id_fkey";
+alter table "public"."input_tags" validate constraint "input_tags_tag_id_fkey";
+alter table "public"."input_tags" validate constraint "input_tags_template_id_fkey";
+alter table "public"."input_tags" validate constraint "input_tags_template_id_fkey1";
+alter table "public"."subject_tags" add constraint "subject_tags_pkey" primary key using index "subject_tags_pkey";
+alter table "public"."subject_tags" add constraint "subject_tags_subject_id_fkey" foreign key (subject_id) references subjects (id) on update cascade on delete cascade not valid;
+alter table "public"."subject_tags" add constraint "subject_tags_tag_id_fkey" foreign key (tag_id) references tags (id) on update cascade on delete cascade not valid;
+alter table "public"."subject_tags" validate constraint "subject_tags_subject_id_fkey";
+alter table "public"."subject_tags" validate constraint "subject_tags_tag_id_fkey";
+alter table "public"."tags" add constraint "tags_name_length" check (((length(name) > 0) and (length(name) < 50))) not valid;
+alter table "public"."tags" add constraint "tags_pkey" primary key using index "tags_pkey";
+alter table "public"."tags" add constraint "tags_team_id_fkey" foreign key (team_id) references teams (id) on update cascade on delete cascade not valid;
+alter table "public"."tags" validate constraint "tags_name_length";
+alter table "public"."tags" validate constraint "tags_team_id_fkey";
+alter table "public"."team_member_subject_roles" add constraint "team_member_subject_roles_pkey" primary key using index "team_member_subject_roles_pkey";
+alter table "public"."team_member_subject_roles" add constraint "team_member_subject_roles_profile_id_fkey" foreign key (profile_id) references profiles (id) on update cascade on delete cascade not valid;
+alter table "public"."team_member_subject_roles" add constraint "team_member_subject_roles_tag_id_fkey" foreign key (subject_id) references subjects (id) on update cascade on delete cascade not valid;
+alter table "public"."team_member_subject_roles" add constraint "team_member_subject_roles_team_id_fkey" foreign key (team_id) references teams (id) on update cascade on delete cascade not valid;
+alter table "public"."team_member_subject_roles" validate constraint "team_member_subject_roles_profile_id_fkey";
+alter table "public"."team_member_subject_roles" validate constraint "team_member_subject_roles_tag_id_fkey";
+alter table "public"."team_member_subject_roles" validate constraint "team_member_subject_roles_team_id_fkey";
+alter table "public"."team_member_tag_roles" add constraint "team_member_tag_roles_pkey" primary key using index "team_member_tag_roles_pkey";
+alter table "public"."team_member_tag_roles" add constraint "team_member_tag_roles_profile_id_fkey" foreign key (profile_id) references profiles (id) on update cascade on delete cascade not valid;
+alter table "public"."team_member_tag_roles" add constraint "team_member_tag_roles_tag_id_fkey" foreign key (tag_id) references tags (id) on update cascade on delete cascade not valid;
+alter table "public"."team_member_tag_roles" add constraint "team_member_tag_roles_team_id_fkey" foreign key (team_id) references teams (id) on update cascade on delete cascade not valid;
+alter table "public"."team_member_tag_roles" validate constraint "team_member_tag_roles_profile_id_fkey";
+alter table "public"."team_member_tag_roles" validate constraint "team_member_tag_roles_tag_id_fkey";
+alter table "public"."team_member_tag_roles" validate constraint "team_member_tag_roles_team_id_fkey";
+alter table "public"."template_tags" add constraint "template_tags_pkey" primary key using index "template_tags_pkey";
+alter table "public"."template_tags" add constraint "template_tags_subject_id_fkey" foreign key (template_id) references subjects (id) on update cascade on delete cascade not valid;
+alter table "public"."template_tags" add constraint "template_tags_tag_id_fkey" foreign key (tag_id) references tags (id) on update cascade on delete cascade not valid;
+alter table "public"."template_tags" add constraint "template_tags_template_id_fkey" foreign key (template_id) references templates (id) on update cascade on delete cascade not valid;
+alter table "public"."template_tags" validate constraint "template_tags_subject_id_fkey";
+alter table "public"."template_tags" validate constraint "template_tags_tag_id_fkey";
+alter table "public"."template_tags" validate constraint "template_tags_template_id_fkey";
+
+-- alter tables
+
+alter table "public"."inputs" alter column "team_id" drop default;
+alter table "public"."subjects" alter column "team_id" drop default;
+alter table "public"."team_members" alter column "team_id" drop default;
+alter table "public"."templates" alter column "team_id" drop default;
+alter table "public"."teams" add column "image_uri" text;
+
+alter table "public"."team_members" add column "role" team_member_role;
+update public.team_members set role = 'owner';
+alter table "public"."team_members" alter column "role" set not null;
+
+-- update functions
+
+create or replace function public.handle_insert_user()
+  returns trigger
+  language plpgsql
+  security definer
+  as $$
+  declare
+    new_team_id uuid;
+  begin
+    insert into public.profiles (id, first_name, last_name)
+      values (new.id, new.raw_user_meta_data ->> 'first_name', new.raw_user_meta_data ->> 'last_name');
+    insert into public.teams (id, name) values ((new.raw_user_meta_data ->> 'team_id')::uuid, coalesce(nullif(new.raw_user_meta_data ->> 'organization', ''), 'Personal')) returning id into new_team_id;
+    insert into public.team_members (team_id, profile_id, role) values (new_team_id, new.id, 'owner');
+    return new;
+  end;
+  $$;
+
+create or replace function public.is_client()
+  returns boolean
+  language plpgsql
+  as $$
+  begin
+    return coalesce((select auth.jwt() -> 'app_metadata' ->> 'is_client')::boolean, false);
+  end;
+  $$;
+
+create or replace function public.can_insert_subject_on_current_plan (subject_id uuid default null)
+  returns boolean
+  language plpgsql
+  as $$
+  declare
+    subject_count int;
+    subscription_status text;
+  begin
+    select count(*)
+      into subject_count
+      from subjects s
+      where s.team_id = (select auth.jwt() -> 'app_metadata' ->> 'active_team_id')::uuid
+        and s.deleted = false
+        and s.archived = false
+        and s.id is distinct from subject_id;
+    select (select auth.jwt() -> 'app_metadata' ->> 'subscription_status')
+      into subscription_status;
+    return (subject_count < 2 or subscription_status = 'active') is true;
+  end;
+  $$;
+
+create or replace function public.handle_insert_or_update_object ()
+  returns trigger
+  language plpgsql
+  security definer
+  set search_path to 'public'
+  as $$
+  begin
+    if (new.bucket_id = 'teams' and storage.filename (new.name) = 'avatar') then
+      update public.teams
+        set image_uri = new.bucket_id || '/' || new.name || '?c=' || substr(md5(random()::text), 0, 5)
+        where id::text = (storage.foldername (new.name))[1];
+    end if;
+    if (new.bucket_id = 'subjects' and storage.filename (new.name) = 'avatar') then
+      update public.subjects
+        set image_uri = new.bucket_id || '/' || new.name || '?c=' || substr(md5(random()::text), 0, 5)
+        where id::text = (storage.foldername (new.name))[1];
+    end if;
+    if (new.bucket_id = 'profiles' and storage.filename (new.name) = 'avatar') then
+      update auth.users
+        set raw_user_meta_data = jsonb_set(raw_user_meta_data, '{image_uri}', to_jsonb (new.bucket_id || '/' || new.name || '?c=' || substr(md5(random()::text), 0, 5)))
+        where id::text = (storage.foldername (new.name))[1];
+    end if;
+    return new;
+  end;
+  $$;
+
+create or replace function public.handle_delete_object ()
+  returns trigger
+  language plpgsql
+  security definer
+  set search_path to 'public'
+  as $$
+  begin
+    if (old.bucket_id = 'teams' and storage.filename (old.name) = 'avatar') then
+      update public.teams
+        set image_uri = null
+        where id::text = (storage.foldername (old.name))[1];
+    end if;
+    if (old.bucket_id = 'subjects' and storage.filename (old.name) = 'avatar') then
+      update public.subjects
+        set image_uri = null
+        where id::text = (storage.foldername (old.name))[1];
+    end if;
+    if (old.bucket_id = 'profiles' and storage.filename (old.name) = 'avatar') then
+      update auth.users
+        set raw_user_meta_data = jsonb_set(raw_user_meta_data, '{image_uri}', 'null'::jsonb)
+        where id::text = (storage.foldername (old.name))[1];
+    end if;
+    return old;
+  end;
+  $$;
+
+create trigger on_delete_object
+  after delete on storage.objects
+  for each row
+  execute function handle_delete_object ();
+
+-- add functions
+
+create or replace function public.upsert_team(_name text, _id uuid default null)
+  returns uuid
+  language plpgsql
+  set search_path = ''
+  as $$
+  begin
+    if _id is null then
+      insert into public.teams (name) values (_name) returning id into _id;
+      insert into public.team_members (team_id, profile_id, role) values (_id, (select auth.uid()::uuid), 'owner'::public.team_member_role);
+    else
+      update public.teams set name = _name where id = _id;
+    end if;
+    return _id;
+  end;
+  $$;
+
+-- nuke rls
+
+drop policy "Owners and team members can delete." on "public"."comments";
+drop policy "Team members & subject managers can insert." on "public"."comments";
+drop policy "Team members & subject managers can select." on "public"."comments";
+drop policy "Team members & subject managers can delete." on "public"."event_inputs";
+drop policy "Team members & subject managers can insert." on "public"."event_inputs";
+drop policy "Team members & subject managers can select." on "public"."event_inputs";
+drop policy "Team members & subject managers can update." on "public"."event_inputs";
+drop policy "Team members & subject managers can select." on "public"."event_type_inputs";
+drop policy "Team members can delete." on "public"."event_type_inputs";
+drop policy "Team members can insert." on "public"."event_type_inputs";
+drop policy "Team members & subject managers can select." on "public"."event_types";
+drop policy "Team members can delete." on "public"."event_types";
+drop policy "Team members can insert." on "public"."event_types";
+drop policy "Team members can update." on "public"."event_types";
+drop policy "Team members & subject managers can insert." on "public"."events";
+drop policy "Team members & subject managers can select." on "public"."events";
+drop policy "Team members & subject managers can update." on "public"."events";
+drop policy "Team members can delete." on "public"."events";
+drop policy "Team members & subject managers can insert." on "public"."input_options";
+drop policy "Team members & subject managers can select." on "public"."input_options";
+drop policy "Team members can delete." on "public"."input_options";
+drop policy "Team members can update." on "public"."input_options";
+drop policy "Team members & subject managers can select." on "public"."input_subjects";
+drop policy "Team members can delete." on "public"."input_subjects";
+drop policy "Team members can insert." on "public"."input_subjects";
+drop policy "Team members & subject managers can insert." on "public"."inputs";
+drop policy "Team members & subject managers can select." on "public"."inputs";
+drop policy "Team members can delete." on "public"."inputs";
+drop policy "Team members can update." on "public"."inputs";
+drop policy "Team members & subject managers can select." on "public"."insights";
+drop policy "Team members can delete." on "public"."insights";
+drop policy "Team members can insert." on "public"."insights";
+drop policy "Team members can update." on "public"."insights";
+drop policy "Owners can delete." on "public"."notifications";
+drop policy "Owners can select." on "public"."notifications";
+drop policy "Owners can update." on "public"."notifications";
+drop policy "Authenticated users can select." on "public"."profiles";
+drop policy "Team members & subject managers can select." on "public"."protocols";
+drop policy "Team members can delete." on "public"."protocols";
+drop policy "Team members can insert." on "public"."protocols";
+drop policy "Team members can update." on "public"."protocols";
+drop policy "Team members & subject managers can select." on "public"."sessions";
+drop policy "Team members can delete." on "public"."sessions";
+drop policy "Team members can insert." on "public"."sessions";
+drop policy "Team members can update." on "public"."sessions";
+drop policy "Authenticated users can select." on "public"."subject_managers";
+drop policy "Team members can delete." on "public"."subject_managers";
+drop policy "Team members = full access." on "public"."subject_notes";
+drop policy "Team members & subject managers can select." on "public"."subjects";
+drop policy "Team members can insert." on "public"."subjects";
+drop policy "Team members can update." on "public"."subjects";
+drop policy "Authenticated users can select." on "public"."team_members";
+drop policy "Select template = full access." on "public"."template_subjects";
+drop policy "Team members = full access." on "public"."templates";
+drop policy "Team members & subject managers can select (subjects)." on "storage"."objects";
+drop policy "Team members can delete (subjects)." on "storage"."objects";
+drop policy "Team members can insert (subjects)." on "storage"."objects";
+drop policy "Team members can update (subjects)." on "storage"."objects";
+drop policy "Everyone can select (profiles)." on "storage"."objects";
+drop policy "Owners can delete (profiles)." on "storage"."objects";
+drop policy "Owners can insert (profiles)." on "storage"."objects";
+drop policy "Owners can update (profiles)." on "storage"."objects";
+
+-- subject manager -> subject client
+
+create table "public"."subject_clients" ("profile_id" uuid not null, "subject_id" uuid not null);
+insert into "public"."subject_clients" select * from "public"."subject_managers";
+alter table "public"."subject_clients" enable row level security;
+create unique index subject_clients_pkey on public.subject_clients using btree (profile_id, subject_id);
+create index subject_clients_subject_id_index on public.subject_clients using btree (subject_id);
+alter table "public"."subject_clients" add constraint "subject_clients_pkey" primary key using index "subject_clients_pkey";
+alter table "public"."subject_clients" add constraint "subject_clients_profile_id_fkey" foreign key (profile_id) references profiles(id) on delete cascade not valid;
+alter table "public"."subject_clients" validate constraint "subject_clients_profile_id_fkey";
+alter table "public"."subject_clients" add constraint "subject_clients_subject_id_fkey" foreign key (subject_id) references subjects(id) on delete cascade not valid;
+alter table "public"."subject_clients" validate constraint "subject_clients_subject_id_fkey";
+
+create or replace function public.join_subject_as_client (share_code text)
+  returns void
+  language plpgsql
+  security definer
+  as $$
+  # variable_conflict use_variable
+  declare
+    existing_client boolean;
+    profile_id uuid;
+    subject_id uuid;
+    team_member_profile_ids uuid[];
+  begin
+    select id into subject_id
+      from subjects
+      where subjects.share_code = share_code;
+    if subject_id is not null then
+      select exists(select true from subject_clients
+        where profile_id = auth.uid() and subject_id = subject_id)
+        into existing_client;
+      if not existing_client then
+        insert into subject_clients (profile_id, subject_id)
+          values (auth.uid (), subject_id);
+        select array_agg(tm.profile_id) into team_member_profile_ids
+          from subjects s join team_members tm on s.team_id = tm.team_id
+          where s.id = subject_id;
+        for profile_id in (select unnest(team_member_profile_ids)) loop
+          insert into notifications (profile_id, type, source_profile_id, source_subject_id)
+            values (profile_id, 'join_subject', auth.uid (), subject_id);
+        end loop;
+      end if;
+    end if;
+  end;
+  $$;
+
+create or replace function public.handle_insert_event ()
+  returns trigger
+  language plpgsql
+  security definer
+  as $$
+  declare
+    curr_session_id uuid;
+    profile_id uuid;
+    subject_client_profile_ids uuid[];
+    team_member_profile_ids uuid[];
+  begin
+    select array_agg(sm.profile_id) into subject_client_profile_ids
+      from subject_clients sm
+      where sm.subject_id = new.subject_id and sm.profile_id <> new.profile_id;
+    select array_agg(tm.profile_id) into team_member_profile_ids
+      from subjects s join team_members tm on s.team_id = tm.team_id
+      where s.id = new.subject_id and tm.profile_id <> new.profile_id;
+    select et.session_id into curr_session_id
+      from event_types et
+      where et.id = new.event_type_id;
+    for profile_id in (
+      select unnest(array_cat(subject_client_profile_ids, team_member_profile_ids))) loop
+        if not exists (
+          select 1
+            from notifications n
+            join events e on n.source_event_id = e.id
+            join event_types et on e.event_type_id = et.id
+              where et.session_id = curr_session_id)
+        then
+          insert into notifications (profile_id, type, source_event_id, source_profile_id, source_subject_id)
+            values (profile_id, 'event', new.id, new.profile_id, new.subject_id);
+        end if;
+      end loop;
+    return null;
+  end;
+  $$;
+
+create or replace function public.handle_insert_comment ()
+  returns trigger
+  language plpgsql
+  security definer
+  as $$
+  declare
+    source_subject_id uuid;
+    subject_client_profile_ids uuid[];
+    team_member_profile_ids uuid[];
+    profile_id uuid;
+  begin
+    select e.subject_id into source_subject_id
+      from events e
+      where e.id = new.event_id;
+    select array_agg(sm.profile_id) into subject_client_profile_ids
+      from subject_clients sm
+      where sm.subject_id = source_subject_id and sm.profile_id <> new.profile_id;
+    select array_agg(tm.profile_id) into team_member_profile_ids
+      from subjects s join team_members tm on s.team_id = tm.team_id
+      where s.id = source_subject_id and tm.profile_id <> new.profile_id;
+    for profile_id in (
+      select unnest(array_cat(subject_client_profile_ids, team_member_profile_ids))) loop
+        insert into notifications (profile_id, type, source_comment_id, source_profile_id, source_subject_id)
+          values (profile_id, 'comment', new.id, new.profile_id, source_subject_id);
+      end loop;
+    return null;
+    end;
+  $$;
+
+-- update data
+
+insert into storage.buckets (id, name, public)
+  values ('teams', 'teams', true);
+
+update auth.users
+  set raw_app_meta_data = jsonb_set(
+    raw_app_meta_data,
+    '{active_team_id}',
+    to_jsonb(users.id),
+    true
+  );
+
+update auth.users
+  set raw_app_meta_data = jsonb_set(
+    raw_app_meta_data,
+    '{is_client}',
+    to_jsonb(coalesce(users.raw_user_meta_data ->> 'is_client', 'false')::boolean),
+    true
+  );
+
+update auth.users
+  set raw_user_meta_data = raw_user_meta_data - 'is_client';
+
+-- drop old stuff
+
+drop trigger if exists on_insert_team on public.teams;
+
+drop function public.handle_insert_team ();
+drop function public.join_subject_as_manager (share_code text);
+
+alter table "public"."subject_managers" drop constraint "subject_managers_profile_id_fkey";
+alter table "public"."subject_managers" drop constraint "subject_managers_subject_id_fkey";
+alter table "public"."subject_managers" drop constraint "subject_managers_pkey";
+alter table "public"."teams" drop column "owner";
+
+drop index if exists "public"."subject_managers_pkey";
+drop index if exists "public"."subject_managers_subject_id_index";
+
+drop table "public"."subject_managers";
+
+-- add new rls policies
+
+create or replace function public.check_tag_roles(
+    _allow_if_role_is_one_of public.team_member_role[],
+    _tag_ids uuid[],
+    _team_id uuid,
+    _user_id uuid
+  )
+  returns boolean
+  language plpgsql
+  as $$
+  declare
+    matched_tag_roles public.team_member_role[] := '{}';
+  begin
+    if array_length(_tag_ids, 1) > 0 then
+      select coalesce(array_agg(tm.role), '{}') into matched_tag_roles
+        from public.team_member_tag_roles tm
+        where tm.profile_id = _user_id
+        and tm.team_id = _team_id
+        and tm.tag_id = any(_tag_ids);
+      if array_length(array(select unnest(matched_tag_roles) intersect select unnest(_allow_if_role_is_one_of)), 1) > 0 then
+        return true;
+      end if;
+    end if;
+    return false;
+  end;
+  $$;
+
+create or replace function public.check_subject_roles(
+    _allow_if_role_is_one_of public.team_member_role[],
+    _subject_id uuid,
+    _team_id uuid,
+    _user_id uuid
+  )
+  returns boolean
+  language plpgsql
+  as $$
+  begin
+    if (
+
+       (select tm.role from public.team_member_subject_roles tm
+         where tm.team_id = _team_id
+           and tm.profile_id = _user_id
+           and tm.subject_id = _subject_id
+         limit 1)
+       = any(_allow_if_role_is_one_of)
+    ) then
+      return true;
+    end if;
+    return false;
+  end;
+  $$;
+
+create or replace function public.authorize(
+    allow_if_is_subject_client boolean default false,
+    allow_if_profile_id_is_user_id boolean default false,
+    allow_if_role_is_one_of public.team_member_role[] default null,
+    allow_if_team_has_no_owner boolean default false,
+    object_input_id uuid default null,
+    object_profile_id uuid default null,
+    object_subject_id uuid default null,
+    object_team_id uuid default null,
+    object_template_id uuid default null
+  )
+  returns boolean
+  language plpgsql
+  security definer
+  set search_path = ''
+  as $$
+  declare
+    object_tag_ids uuid[];
+    team_has_owner boolean;
+    user_id uuid;
+  begin
+    select auth.uid()::uuid into user_id;
+
+    -- Check if profile ID matches user ID
+    if allow_if_profile_id_is_user_id and object_profile_id is not null and user_id = object_profile_id then
+      return true;
+    end if;
+
+    -- Check if the user is a subject client
+    if allow_if_is_subject_client and object_subject_id is not null and exists (
+      select 1 from public.subject_clients sc where sc.subject_id = object_subject_id and sc.profile_id = user_id
+    ) then
+      return true;
+    end if;
+
+    -- Resolve the team ID if necessary
+    if allow_if_role_is_one_of is not null and array_length(allow_if_role_is_one_of, 1) > 0 then
+      if object_team_id is null then
+        if object_subject_id is not null then
+          select s.team_id into object_team_id
+            from public.subjects s
+            where s.id = object_subject_id;
+        end if;
+        if object_input_id is not null then
+          select i.team_id into object_team_id
+            from public.inputs i
+            where i.id = object_input_id;
+        end if;
+        if object_template_id is not null then
+          select t.team_id into object_team_id
+            from public.templates t
+            where t.id = object_template_id;
+        end if;
+      end if;
+
+      -- Check if the user has one of the allowed roles in the team
+      if object_team_id is not null then
+        if (
+          select tm.role from public.team_members tm
+            where tm.team_id = object_team_id
+            and tm.profile_id = user_id
+            limit 1
+        ) = any(allow_if_role_is_one_of) then
+          return true;
+        end if;
+
+        -- Additional checks for subjects and tags
+        if object_subject_id is not null then
+          if public.check_subject_roles(
+            _allow_if_role_is_one_of => allow_if_role_is_one_of,
+            _subject_id => object_subject_id,
+            _team_id => object_team_id,
+            _user_id => user_id
+          ) then
+            return true;
+          end if;
+          select array_agg(tm.tag_id) into object_tag_ids
+            from public.subject_tags tm
+            where tm.subject_id = object_subject_id;
+          if public.check_tag_roles(
+            _allow_if_role_is_one_of => allow_if_role_is_one_of,
+            _tag_ids => object_tag_ids,
+            _team_id => object_team_id,
+            _user_id => user_id
+          ) then
+            return true;
+          end if;
+        end if;
+
+        -- Additional checks for inputs and tags
+        if object_input_id is not null then
+          select array_agg(it.tag_id) into object_tag_ids
+            from public.input_tags it
+            where it.input_id = object_input_id;
+          if public.check_tag_roles(
+            _allow_if_role_is_one_of => allow_if_role_is_one_of,
+            _tag_ids => object_tag_ids,
+            _team_id => object_team_id,
+            _user_id => user_id
+          ) then
+            return true;
+          end if;
+        end if;
+
+        -- Additional checks for templates and tags
+        if object_template_id is not null then
+          select array_agg(it.tag_id) into object_tag_ids
+            from public.template_tags it
+            where it.template_id = object_template_id;
+          if public.check_tag_roles(
+            _allow_if_role_is_one_of => allow_if_role_is_one_of,
+            _tag_ids => object_tag_ids,
+            _team_id => object_team_id,
+            _user_id => user_id
+          ) then
+            return true;
+          end if;
+        end if;
+      end if;
+    end if;
+
+    -- Check if the team has no owner, if allowed
+    if allow_if_team_has_no_owner and object_team_id is not null then
+      select exists (
+        select 1 from public.team_members tm
+        where tm.team_id = object_team_id
+        and tm.role = 'owner'
+      ) into team_has_owner;
+
+      if not team_has_owner then
+        return true;
+      end if;
+    end if;
+
+    -- Handle inputs linked to event types
+    if object_input_id is not null then
+      for object_subject_id in
+        select distinct et.subject_id
+          from public.event_type_inputs eti
+          join public.event_types et on et.id = eti.event_type_id
+          where eti.input_id = object_input_id
+      loop
+        if public.authorize(
+          allow_if_is_subject_client => allow_if_is_subject_client,
+          allow_if_role_is_one_of => allow_if_role_is_one_of,
+          object_subject_id => object_subject_id
+        ) then
+          return true;
+        end if;
+      end loop;
+    end if;
+
+    return false;
+  end;
+  $$;
+
+create or replace function public.get_subject_id_from_event_id(event_id uuid)
+  returns uuid
+  language plpgsql
+  as $$
+  begin
+    return (select event_types.subject_id
+      from events
+      join event_types on events.event_type_id = event_types.id
+      where events.id = event_id);
+  end;
+  $$;
+
+create or replace function public.get_subject_id_from_event_type_id(event_type_id uuid)
+  returns uuid
+  language plpgsql
+  as $$
+  begin
+    return (select event_types.subject_id
+      from event_types
+      where event_types.id = event_type_id);
+  end;
+  $$;
+
+create or replace function public.get_subject_id_from_protocol_id(protocol_id uuid)
+  returns uuid
+  language plpgsql
+  as $$
+  begin
+    return (select protocols.subject_id
+      from protocols
+      where protocols.id = protocol_id);
+  end;
+  $$;
+
+create policy "select_policy" on "public"."comments" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_event_id(event_id)))));
+
+create policy "insert_policy" on "public"."comments" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_event_id(event_id)))));
+
+create policy "delete_policy" on "public"."comments" for delete to authenticated using ((select public.authorize(
+  allow_if_profile_id_is_user_id => true,
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_profile_id => profile_id,
+  object_subject_id => (select public.get_subject_id_from_event_id(event_id)))));
+
+create policy "select_policy" on "public"."event_inputs" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_event_id(event_id)))));
+
+create policy "insert_policy" on "public"."event_inputs" for insert to authenticated with check ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_event_id(event_id)))));
+
+create policy "update_policy" on "public"."event_inputs" for update to authenticated
+  using ((select public.authorize(
+    allow_if_is_subject_client => true,
+    allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+    object_subject_id => (select public.get_subject_id_from_event_id(event_id)))))
+  with check ((select public.authorize(
+    allow_if_is_subject_client => true,
+    allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+    object_subject_id => (select public.get_subject_id_from_event_id(event_id)))));
+
+create policy "delete_policy" on "public"."event_inputs" for delete to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_event_id(event_id)))));
+
+create policy "select_policy" on "public"."event_type_inputs" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_event_type_id(event_type_id)))));
+
+create policy "insert_policy" on "public"."event_type_inputs" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_event_type_id(event_type_id)))));
+
+create policy "delete_policy" on "public"."event_type_inputs" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_event_type_id(event_type_id)))));
+
+create policy "select_policy" on "public"."event_types" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "insert_policy" on "public"."event_types" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "update_policy" on "public"."event_types" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => subject_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => subject_id)));
+
+create policy "delete_policy" on "public"."event_types" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "select_policy" on "public"."events" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "insert_policy" on "public"."events" for insert to authenticated with check ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "update_policy" on "public"."events" for update to authenticated
+  using ((select public.authorize(
+    allow_if_is_subject_client => true,
+    allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+    object_subject_id => subject_id)))
+  with check ((select public.authorize(
+    allow_if_is_subject_client => true,
+    allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+    object_subject_id => subject_id)));
+
+create policy "delete_policy" on "public"."events" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "select_policy" on "public"."input_options" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_input_id => input_id)));
+
+create policy "insert_policy" on "public"."input_options" for insert to authenticated with check ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+  object_input_id => input_id)));
+
+create policy "update_policy" on "public"."input_options" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_input_id => input_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_input_id => input_id)));
+
+create policy "delete_policy" on "public"."input_options" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_input_id => input_id)));
+
+create policy "select_policy" on "public"."input_subjects" for select to authenticated using (true);
+
+create policy "insert_policy" on "public"."input_subjects" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_input_id => input_id)));
+
+create policy "delete_policy" on "public"."input_subjects" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_input_id => input_id)));
+
+create policy "select_policy" on "public"."input_tags" for select to authenticated using (true);
+
+create policy "insert_policy" on "public"."input_tags" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_input_id => input_id)));
+
+create policy "update_policy" on "public"."input_tags" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_input_id => input_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_input_id => input_id)));
+
+create policy "delete_policy" on "public"."input_tags" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_input_id => input_id)));
+
+create policy "select_policy" on "public"."inputs" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_input_id => id,
+  object_team_id => team_id)));
+
+create policy "insert_policy" on "public"."inputs" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "update_policy" on "public"."inputs" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)));
+
+create policy "delete_policy" on "public"."inputs" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "select_policy" on "public"."insights" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "insert_policy" on "public"."insights" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "update_policy" on "public"."insights" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => subject_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => subject_id)));
+
+create policy "delete_policy" on "public"."insights" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "select_policy" on "public"."notifications" for select to authenticated using ((select public.authorize(
+  allow_if_profile_id_is_user_id => true,
+  object_profile_id => profile_id)));
+
+create policy "delete_policy" on "public"."notifications" for delete to authenticated using ((select public.authorize(
+  allow_if_profile_id_is_user_id => true,
+  object_profile_id => profile_id)));
+
+create policy "select_policy" on "public"."profiles" for select to authenticated using (true);
+
+create policy "select_policy" on "public"."protocols" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "insert_policy" on "public"."protocols" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "update_policy" on "public"."protocols" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => subject_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => subject_id)));
+
+create policy "delete_policy" on "public"."protocols" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "select_policy" on "public"."sessions" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_protocol_id(protocol_id)))));
+
+create policy "insert_policy" on "public"."sessions" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_protocol_id(protocol_id)))));
+
+create policy "update_policy" on "public"."sessions" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => (select public.get_subject_id_from_protocol_id(protocol_id)))))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => (select public.get_subject_id_from_protocol_id(protocol_id)))));
+
+create policy "delete_policy" on "public"."sessions" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => (select public.get_subject_id_from_protocol_id(protocol_id)))));
+
+create policy "select_policy" on "public"."subject_clients" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "delete_policy" on "public"."subject_clients" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "select_policy" on "public"."subject_notes" for select to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => id)));
+
+create policy "insert_policy" on "public"."subject_notes" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+  object_subject_id => id)));
+
+create policy "update_policy" on "public"."subject_notes" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+    object_subject_id => id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin', 'recorder']::public.team_member_role[],
+    object_subject_id => id)));
+
+create policy "select_policy" on "public"."subject_tags" for select to authenticated using (true);
+
+create policy "insert_policy" on "public"."subject_tags" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "update_policy" on "public"."subject_tags" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => subject_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => subject_id)));
+
+create policy "delete_policy" on "public"."subject_tags" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_subject_id => subject_id)));
+
+create policy "select_policy" on "public"."subjects" for select to authenticated using ((select public.authorize(
+  allow_if_is_subject_client => true,
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  object_subject_id => id,
+  object_team_id => team_id)));
+
+create policy "insert_policy" on "public"."subjects" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "update_policy" on "public"."subjects" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)));
+
+create policy "select_policy" on "public"."tags" for select to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "insert_policy" on "public"."tags" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "update_policy" on "public"."tags" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)));
+
+create policy "delete_policy" on "public"."tags" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "select_policy" on "public"."team_member_subject_roles" for select to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "insert_policy" on "public"."team_member_subject_roles" for insert to authenticated with check (
+  role != 'owner'::public.team_member_role and
+  (select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)));
+
+create policy "update_policy" on "public"."team_member_subject_roles" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)))
+  with check (
+    role != 'owner'::public.team_member_role and
+    (select public.authorize(
+      allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+      object_team_id => team_id)));
+
+create policy "delete_policy" on "public"."team_member_subject_roles" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "select_policy" on "public"."team_member_tag_roles" for select to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "insert_policy" on "public"."team_member_tag_roles" for insert to authenticated with check (
+  role != 'owner'::public.team_member_role and
+  (select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)));
+
+create policy "update_policy" on "public"."team_member_tag_roles" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)))
+  with check (
+    role != 'owner'::public.team_member_role and
+    (select public.authorize(
+      allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+      object_team_id => team_id)));
+
+create policy "delete_policy" on "public"."team_member_tag_roles" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "select_policy" on "public"."team_members" for select to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "insert_policy" on "public"."team_members" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of =>
+    case
+      when role = 'owner' then array['owner']::public.team_member_role[]
+      else array['owner', 'admin']::public.team_member_role[]
+    end,
+  allow_if_team_has_no_owner => true,
+  object_team_id => team_id)));
+
+create policy "update_policy" on "public"."team_members" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of =>
+      case
+        when role = 'owner' then array['owner']::public.team_member_role[]
+        else array['owner', 'admin']::public.team_member_role[]
+      end,
+    object_team_id => team_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of =>
+      case
+        when role = 'owner' then array['owner']::public.team_member_role[]
+        else array['owner', 'admin']::public.team_member_role[]
+      end,
+    object_team_id => team_id)));
+
+create policy "delete_policy" on "public"."team_members" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "select_policy" on "public"."teams" for select to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+  allow_if_team_has_no_owner => true,
+  object_team_id => id)));
+
+create policy "insert_policy" on "public"."teams" for insert to authenticated with check (true);
+
+create policy "update_policy" on "public"."teams" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => id)));
+
+create policy "select_policy" on "public"."template_subjects" for select to authenticated using (true);
+
+create policy "insert_policy" on "public"."template_subjects" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_template_id => template_id)));
+
+create policy "delete_policy" on "public"."template_subjects" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_template_id => template_id)));
+
+create policy "select_policy" on "public"."template_tags" for select to authenticated using (true);
+
+create policy "insert_policy" on "public"."template_tags" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_template_id => template_id)));
+
+create policy "update_policy" on "public"."template_tags" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_template_id => template_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_template_id => template_id)));
+
+create policy "delete_policy" on "public"."template_tags" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_template_id => template_id)));
+
+create policy "select_policy" on "public"."templates" for select to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "insert_policy" on "public"."templates" for insert to authenticated with check ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "update_policy" on "public"."templates" for update to authenticated
+  using ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)))
+  with check ((select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_team_id => team_id)));
+
+create policy "delete_policy" on "public"."templates" for delete to authenticated using ((select public.authorize(
+  allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+  object_team_id => team_id)));
+
+create policy "subjects_select_policy" on "storage"."objects" as permissive for select to authenticated using (
+  (bucket_id = 'subjects'::text) and
+  (select public.authorize(
+    allow_if_is_subject_client => true,
+    allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+    object_subject_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "subjects_insert_policy" on "storage"."objects" as permissive for insert to authenticated with check (
+  (bucket_id = 'subjects'::text) and
+  (select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "subjects_update_policy" on "storage"."objects" as permissive for update to authenticated
+  using (
+    (bucket_id = 'subjects'::text) and
+    (select public.authorize(
+      allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+      object_subject_id => ((storage.foldername (objects.name))[1])::uuid)))
+  with check (
+    (bucket_id = 'subjects'::text) and
+    (select public.authorize(
+      allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+      object_subject_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "subjects_delete_policy" on "storage"."objects" as permissive for delete to authenticated using (
+  (bucket_id = 'subjects'::text) and
+  (select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin']::public.team_member_role[],
+    object_subject_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "profiles_select_policy" on "storage"."objects" as permissive for select to authenticated using
+  (bucket_id = 'profiles'::text);
+
+create policy "profiles_insert_policy" on "storage"."objects" as permissive for insert to authenticated with check (
+  (bucket_id = 'profiles'::text) and
+  (select public.authorize(
+    allow_if_profile_id_is_user_id => true,
+    object_profile_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "profiles_update_policy" on "storage"."objects" as permissive for update to authenticated
+  using (
+    (bucket_id = 'profiles'::text) and
+    (select public.authorize(
+      allow_if_profile_id_is_user_id => true,
+      object_profile_id => ((storage.foldername (objects.name))[1])::uuid)))
+  with check (
+    (bucket_id = 'profiles'::text) and
+    (select public.authorize(
+      allow_if_profile_id_is_user_id => true,
+      object_profile_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "profiles_delete_policy" on "storage"."objects" as permissive for delete to authenticated using (
+  (bucket_id = 'profiles'::text) and
+  (select public.authorize(
+    allow_if_profile_id_is_user_id => true,
+    object_profile_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "teams_select_policy" on "storage"."objects" as permissive for select to authenticated using (
+  (bucket_id = 'teams'::text) and
+  (select public.authorize(
+    allow_if_role_is_one_of => array['owner', 'admin', 'recorder', 'viewer']::public.team_member_role[],
+    object_team_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "teams_insert_policy" on "storage"."objects" as permissive for insert to authenticated with check (
+  (bucket_id = 'teams'::text) and
+  (select public.authorize(
+    allow_if_role_is_one_of => array['owner']::public.team_member_role[],
+    object_team_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "teams_update_policy" on "storage"."objects" as permissive for update to authenticated
+  using (
+    (bucket_id = 'teams'::text) and
+    (select public.authorize(
+      allow_if_role_is_one_of => array['owner']::public.team_member_role[],
+      object_team_id => ((storage.foldername (objects.name))[1])::uuid)))
+  with check (
+    (bucket_id = 'teams'::text) and
+    (select public.authorize(
+      allow_if_role_is_one_of => array['owner']::public.team_member_role[],
+      object_team_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+create policy "teams_delete_policy" on "storage"."objects" as permissive for delete to authenticated using (
+  (bucket_id = 'teams'::text) and
+  (select public.authorize(
+    allow_if_role_is_one_of => array['owner']::public.team_member_role[],
+    object_team_id => ((storage.foldername (objects.name))[1])::uuid)));
+
+-- update indexes
+
+alter table "public"."subject_clients" drop constraint "subject_clients_pkey";
+drop index if exists "public"."subject_clients_subject_id_index";
+drop index if exists "public"."team_members_team_id_index";
+drop index if exists "public"."subject_clients_pkey";
+create unique index subject_clients_pkey on public.subject_clients using btree (subject_id, profile_id);
+alter table "public"."subject_clients" add constraint "subject_clients_pkey" primary key using index "subject_clients_pkey";
+create index notifications_profile_id_created_at_index on public.notifications using btree (profile_id, created_at);
diff --git a/tailwind.css b/tailwind.css
index 2e154db5..1fcdb2d4 100644
--- a/tailwind.css
+++ b/tailwind.css
@@ -43,7 +43,7 @@
 
   /* hack to left align ios date inputs */
   input::-webkit-date-and-time-value {
-    text-align: left;
+    @apply text-left;
   }
 
   /* hack to style observable plot titles */
@@ -61,6 +61,7 @@
     @apply fill-bg-1 stroke-0 text-fg-1;
   }
 
+  /* hack to style observable plot swatches */
   figure[class^='plot-'] div[class$='-swatches-wrap'] {
     @apply gap-1 px-4 pt-4;
   }