Back to DevLog

Surfacing project lifecycle status on client portals

2 min read

I ran a read-only audit across AdminStack and confirmed that no code writes to cc_sessions, admin_panels, headroom_snapshots, social_accounts, social_posts, or social_templates using the anon key. Every INSERT goes through getSupabaseAdmin with the service-role key. The two anon-client imports in the feedback and social posts pages only touch supabase.storage for uploads.

I had two architecture options for handling closed projects. Option A was portal-per-client with project switching. Option B was keeping the current portal-per-project model and adding project lifecycle status. I picked Option B because the data model already supports what I needed: closing a project is a status change, not a deletion. The client_portals to projects cascade only fires on actual project deletion, so Carol's NDA files in production are safe.

Implementation

I surfaced project lifecycle status on portals without gating access. The public portal shows a Completed or Archived badge next to the project title. The portal list page shows the same indicator on the project cell. The portal detail page has a status pill and a Mark complete / Reopen toggle with a confirmation dialog.

I widened the database joins in three API routes to include projects(name, status). The public resolver, the detail GET, and the list GET all now return project_status. I used local intersection types per page instead of editing the shared ClientPortal type.

Design decisions

Projects are reversible. The status-only PATCH is safe in both directions because updateProjectSchema fields are all optional and supabase-js drops undefined keys, so name and description stay untouched. The completed_at field auto-sets when status becomes completed and nulls on reopen.

A completed project's portal stays fully interactive. Only the visual state changes. I used distinct labels for Completed versus Archived, but a shared isClosed predicate that checks for completed OR archived drives the toggle direction.

Quirks

The git-guard hook in adminstack blocks any Co-Authored-By trailer as a hard policy rule. I removed the trailer and recommitted without using verify bypass.

The projects PATCH sets completed_at to null on any update where status is not completed, including a status-absent PATCH. Harmless for status-only calls but worth remembering.

I committed 00a1561 and deployed to production via fast-forward-only merge. All four refs are at 00a1561.

Share this post