diff --git a/package.json b/package.json index f58036b..e43a3c8 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "ai": "^6.0.197", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.4.0", "dexie": "^4.4.3", "dexie-react-hooks": "^4.4.0", "framer-motion": "^11.18.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b64e8b..9043c78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,7 +19,7 @@ importers: version: 3.0.199(react@19.2.7)(zod@4.4.3) '@base-ui/react': specifier: ^1.5.0 - version: 1.5.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 1.5.0(@types/react@19.2.16)(date-fns@4.4.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@ducanh2912/next-pwa': specifier: ^10.2.9 version: 10.2.9(next@16.2.7(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(webpack@5.107.2(postcss@8.5.15)) @@ -71,6 +71,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + date-fns: + specifier: ^4.4.0 + version: 4.4.0 dexie: specifier: ^4.4.3 version: 4.4.3 @@ -2901,6 +2904,9 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} + date-fns@4.4.0: + resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -6039,7 +6045,7 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@base-ui/react@1.5.0(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@base-ui/react@1.5.0(@types/react@19.2.16)(date-fns@4.4.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@babel/runtime': 7.29.7 '@base-ui/utils': 0.2.9(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) @@ -6050,6 +6056,7 @@ snapshots: use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: '@types/react': 19.2.16 + date-fns: 4.4.0 '@base-ui/utils@0.2.9(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: @@ -8181,6 +8188,8 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 + date-fns@4.4.0: {} + debug@3.2.7: dependencies: ms: 2.1.3 diff --git a/supabase/migrations/0007_add_client_portal_tables.sql b/supabase/migrations/0007_add_client_portal_tables.sql new file mode 100644 index 0000000..eca26ae --- /dev/null +++ b/supabase/migrations/0007_add_client_portal_tables.sql @@ -0,0 +1,110 @@ +-- 0007: Add Client Portal Tables and Roles +-- Run after: 0006_add_pgvector_and_embeddings.sql + +-- 1. Update Profiles with Role +ALTER TABLE public.profiles ADD COLUMN IF NOT EXISTS role text DEFAULT 'freelancer'::text CHECK (role IN ('freelancer', 'client')); + +-- 2. Update Clients to link to Client Auth ID +ALTER TABLE public.clients ADD COLUMN IF NOT EXISTS client_auth_id uuid REFERENCES auth.users(id) ON DELETE SET NULL; + +-- 3. Update Tasks to add Client Visibility +ALTER TABLE public.tasks ADD COLUMN IF NOT EXISTS is_public_to_client boolean DEFAULT false; + +-- 4. Create Project Revisions table +CREATE TABLE IF NOT EXISTS public.project_revisions ( + id uuid DEFAULT uuid_generate_v4() PRIMARY KEY, + project_id uuid REFERENCES public.projects(id) ON DELETE CASCADE NOT NULL, + client_id uuid REFERENCES public.clients(id) ON DELETE CASCADE NOT NULL, + requested_by uuid REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL, + description text NOT NULL, + status text DEFAULT 'pending'::text CHECK (status IN ('pending', 'in_progress', 'completed', 'rejected')), + created_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL, + updated_at timestamp with time zone DEFAULT timezone('utc'::text, now()) NOT NULL +); + +-- Trigger to set updated_at +CREATE TRIGGER set_project_revisions_updated_at +BEFORE UPDATE ON public.project_revisions +FOR EACH ROW +EXECUTE FUNCTION public.set_updated_at(); + +-- 5. Enable RLS +ALTER TABLE public.project_revisions ENABLE ROW LEVEL SECURITY; + +-- 6. Add RLS Policies for Client Access + +-- PROFILES +-- Clients can read their own profile +CREATE POLICY "Clients can view own profile" +ON public.profiles FOR SELECT +USING (auth.uid() = id); + +-- CLIENTS +-- Clients can read their own client record +CREATE POLICY "Clients can view own client record" +ON public.clients FOR SELECT +USING (client_auth_id = auth.uid()); + +-- PROJECTS +-- Clients can view projects where client_id matches their client record +CREATE POLICY "Clients can view own projects" +ON public.projects FOR SELECT +USING ( + client_id IN ( + SELECT id FROM public.clients WHERE client_auth_id = auth.uid() + ) +); + +-- TASKS +-- Clients can view tasks linked to their projects IF is_public_to_client is true +CREATE POLICY "Clients can view public tasks of their projects" +ON public.tasks FOR SELECT +USING ( + is_public_to_client = true + AND project_id IN ( + SELECT id FROM public.projects WHERE client_id IN ( + SELECT id FROM public.clients WHERE client_auth_id = auth.uid() + ) + ) +); + +-- PROJECT PLANNING SECTIONS (Milestones, Roadmap, etc.) +-- Clients can view planning sections of their projects +CREATE POLICY "Clients can view planning sections of their projects" +ON public.project_planning_sections FOR SELECT +USING ( + project_id IN ( + SELECT id FROM public.projects WHERE client_id IN ( + SELECT id FROM public.clients WHERE client_auth_id = auth.uid() + ) + ) +); + +-- PROJECT REVISIONS +-- Freelancers can manage all revisions for their projects +CREATE POLICY "Freelancers can manage revisions" +ON public.project_revisions FOR ALL +USING ( + project_id IN ( + SELECT id FROM public.projects WHERE user_id = auth.uid() + ) +); + +-- Clients can insert revisions for their projects +CREATE POLICY "Clients can insert revisions" +ON public.project_revisions FOR INSERT +WITH CHECK ( + client_id IN ( + SELECT id FROM public.clients WHERE client_auth_id = auth.uid() + ) + AND requested_by = auth.uid() +); + +-- Clients can view their own revisions +CREATE POLICY "Clients can view own revisions" +ON public.project_revisions FOR SELECT +USING ( + client_id IN ( + SELECT id FROM public.clients WHERE client_auth_id = auth.uid() + ) +);