Skip to content

Projects Dashboard & Lifecycle

This area covers 26 features. Watch the walkthrough, then use the reference below — each feature links to the exact moment it appears (▶).

Who this is for: Project lead / owner. Each feature below lists the role/permission it requires.

Features

Create project ▶ 00:06

As an org member, I want to create a new translation project, so that my team can start translating content.

How it works. User clicks '+ New Project' button on dashboard. Dialog opens with fields: project name (required), source language (required, supports BCP-47 or descriptive label), target language (required unless shape is 'source-only'). Advanced disclosure reveals project shape radio options (self-contained / source-only / linked-target). Submit validates name + source required, creates server-side project row first (POST /api/v2/projects), then persists language metadata. On success, navigates to project. On error, shows error message and preserves form.

WhoProject lead / owner PermissionsManage members/invites: project_lead(500); role changes: maintainer/owner(≥600); link role capped at contributor(400)
Key files

src/components/ProjectCreateDialog.tsx:50-131
src/lib/sync/cloud-projects.ts:59-78
src/lib/sync/project-settings.ts (patchProjectSettings call)

View project list (Org Home) ▶ 00:00

As an org member, I want to see all projects in my organization on a dashboard, so that I can find and open projects to work on.

How it works. Dashboard displays projects in single org or all orgs (via switcher). Shows rollup stats: total projects, average translation %, average validation %, stalled count, overdue count. Projects listed with progress bars (yellow=translated %, green=validated %). Each row shows: project name, role badge, status (translated % or 'Stalled' or 'Not started'), validation %, audio %. Sorting/filtering available: search by name, status filter (All/Stalled/Overdue), sort by (Recently updated / Needs attention / Least translated / Most progress / Name). Pagination handled by visible array slicing. Empty state: 'No projects in this org yet.'

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/OrgHome.tsx:161-917
src/lib/frontier/portfolio.ts (getPortfolio, translatedPct, validatedPct, audioPct, deadlineStatus, activityStatus)

Filter projects by name ▶ 00:00

As an org member, I want to search projects by name, so that I can quickly find a specific project.

How it works. Text input 'Filter projects…' updates in real-time. Fuzzy search not implemented—exact substring match (case-insensitive). Clear button (X) appears when input has text. Pressing Escape clears focus. Filtered results update instantly. Empty state: 'No matching projects.' if no results.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/OrgHome.tsx:176,381-382,549-573

Filter projects by status ▶ 00:00

As a manager, I want to filter projects by status (stalled, overdue), so that I can focus on projects needing attention.

How it works. Pill-style toggle buttons: All (default), Stalled, Overdue. Clicking a pill sets aria-pressed=true on that pill, false on others. 'Stalled' = activity stopped 14+ days ago. 'Overdue' = deadline in past. Filter updates visible list without affecting rollup stats above. Empty state shows appropriate message (e.g., 'No projects need attention yet.' for 'Needs attention' lens, 'No overdue projects.' for 'Overdue' filter).

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/OrgHome.tsx:34,56-60,178,383-391,576-595,769-784

Sort projects by lens (view option) ▶ 00:12

As a user, I want to sort projects by different criteria (recent, attention, progress), so that I can view projects in the order most relevant to my workflow.

How it works. Dropdown menu (Select component) or pill-style buttons (when isAllOrgs=true) with options: Recently updated, Needs attention, Least translated, Most progress, Name. Selected option persists in localStorage (PROJECT_LENS_STORAGE_KEY). When changed, list re-sorts client-side. Each lens has a description and empty-state message. Sorting logic in sortProjectsByLens() function. Projects within each lens sub-sorted alphabetically by name.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/OrgHome.tsx:36-93,95-114,144-159,179,357-365,598-616,788-806

Archive project ▶ 00:00

As an owner, I want to archive a project, so that completed or obsolete projects don't clutter my dashboard.

How it works. Project owner clicks '⋯' (overflow menu) on project overview → selects 'Archive'. API call POST /api/v2/projects/:id/archive. On success (200), project gets archivedAt timestamp + archivedBy metadata. Router navigates to /projects (active list). Archived project no longer appears in active list. Error handling: 403 = forbidden (non-owner attempts archive); user sees error message. 404 = local-only project (no server row); treated as success (local-only).

WhoProject lead / owner PermissionsLifecycle ops: project_lead(500)+ / owner(700)
Key files

src/components/org/ProjectOverview.tsx:305-318,422-430
src/lib/sync/archive.ts:67-94

View archived projects ▶ 00:00

As an owner, I want to see archived projects in a dedicated view, so that I can manage or restore them if needed.

How it works. Navigate to /projects/archived. Component ArchivedProjects fetches archived projects for active org (GET /api/v2/projects?orgId=N&archived=true). Displays list: project name, restore button. Selecting org from sidebar reloads archived projects for that org. Empty state: 'No archived projects.' Load state: 'Loading…' Error state: displays error message. Must select org first—if activeOrgId=null, shows 'Select an organization' message.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ArchivedProjects.tsx:10-85
src/lib/sync/cloud-projects.ts:150-166

Restore archived project ▶ 00:00

As an owner, I want to restore an archived project, so that I can reactivate it if the work continues.

How it works. On archived projects page, user clicks 'Restore' button for a project. API call DELETE /api/v2/projects/:id/archive. On success (200), archivedAt/archivedBy cleared. Project removed from archived list. User can navigate to /projects to see it in active list. Error handling: 403 = forbidden (non-owner); 404 = local-only; error message displayed in-place. No navigation on restore—list updates in-place.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ArchivedProjects.tsx:34-45,71-76
src/lib/sync/archive.ts:96-114

Set project deadline ▶ 00:00

As a manager, I want to set a deadline for a project, so that the team knows when the work is due.

How it works. Project overview → Deadline section. Initially shows 'No deadline set'. Maintainer+ clicks 'Set deadline'. Date picker appears. User selects date (ISO format YYYY-MM-DD). Clicks 'Save'. API call PATCH /api/v2/projects/:id/deadline with {deadline: '2024-12-31'}. On success, deadline displays with status chip ('On track' / 'Due soon' / 'Overdue'). User can click 'Change' to edit or 'Clear' to remove. Deadline appears on project cards (if within 7 days: 'Due soon'; if past: 'Overdue'; otherwise not shown).

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:197-198,272-285,576-635
src/lib/sync/cloud-projects.ts:172-184

Status chip (deadline indicator) ▶ 00:00

As a user, I want to see at a glance whether a project is on track or overdue, so that I can prioritize my work.

How it works. On project card in dashboard and project overview: status badge appears next to title. Colors and labels: 'On track' (emerald, if deadline exists and is >7 days away), 'Due soon' (amber, if deadline is 0-7 days), 'Overdue' (red, if deadline in past). Derived from deadlineStatus(project, now) enum ('ok'|'soon'|'overdue'|null). StatusChip component returns null if status='no-deadline' (no deadline set or no progress).

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:28-64
src/lib/frontier/portfolio.ts (deadlineStatus function)

Project lifecycle toggle (mark inactive/active) ▶ 00:00

As a project manager, I want to freeze a project to prevent edits, so that completed work isn't accidentally modified.

How it works. Project overview → overflow menu (⋯) → 'Mark as Inactive'. API call PATCH /api/v2/projects/:id/lifecycle with {isActive: false}. On success, project frozen: editing disabled everywhere, banner appears at top of workspace ('Project X is inactive — cannot be edited until reactivated'). Project can be marked Active again via same menu. Banner shows 'Reactivate' button (project_lead+ only). Non-authorized users see read-only banner without button. isActive=false persists on project record.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/hooks/useProjectLifecycle.ts:34-77
src/components/InactiveProjectBanner.tsx:51-83
src/lib/sync/cloud-projects.ts:271-286
src/components/org/ProjectOverview.tsx:250-254,348-355,414-420

Assign work to team member ▶ 00:00

As a manager, I want to assign a specific book or chapter to a team member with a deadline, so that my team knows what to work on.

How it works. Project overview → Team card → 'Assign…' button. Expands into form: (1) member dropdown (fetched from org), (2) book/file dropdown, (3) chapter dropdown (loaded from file's chapters via canonical_ref LIKE matching), (4) optional deadline date picker, (5) Assign button. Submit creates assignment via POST (createAssignment). On success, shows success message 'Assigned [scope] to [username].' and refreshes workload display. On error, shows error message. Cancel button collapses form. Team card shows open assignments per member + completion %.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/AssignWork.tsx:32-199
src/lib/sync/assignments.ts (createAssignment, getFileChapters, getMyAssignmentsForOrg)

View team workload on project overview ▶ 00:00

As a manager, I want to see which team members have assignments and their progress, so that I can monitor task completion.

How it works. Project overview → Team card. Shows list of members with open assignments. For each: username (truncated with tooltip), progress bar (cells done / cells total), completion %, open assignment count. Members with no open assignments not listed. Empty state: 'No open assignments in this project yet.' Clicking on a member's row does not navigate (static display). Fetched via getProjectAssignments(jwt, projectId) on component mount.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:200,225-230,641-678

View members & manage access (project overview) ▶ 00:00

As a manager, I want to add/remove project members and change roles from the overview, so that I don't need to open the full project to manage access.

How it works. Project overview → Members card (maintainer+ only, not shown if archived). Renders MembersTab component inline (same UI as full members page). Shows: member list with role badges, add member button, remove/role-change controls. Adding member invites via email or @username. Changes apply immediately (no modal—direct edit). Same access rules as in-project members page.

WhoProject lead / owner PermissionsManage members/invites: project_lead(500); role changes: maintainer/owner(≥600); link role capped at contributor(400)
Key files

src/components/org/ProjectOverview.tsx:683-688
src/components/ProjectMembersPage (imported)

Download project bundle (export) ▶ 00:00

As a manager, I want to export a completed project's data, so that I can deliver it to stakeholders or archive it externally.

How it works. Project overview → overflow menu (⋯) → 'Download deliverable'. Calls downloadProjectBundle(projectId, projectName, jwt, fileId). User's browser receives a ZIP file containing project data. Success: file downloaded silently. Error: message displayed ('This project has no files to export yet.' if no files; or network error message).

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:287-303,406-412
src/lib/sync/export-bundle.ts (downloadProjectBundle)

Pending invitations inbox ▶ 00:00

As a user, I want to see invitations I've received but haven't accepted yet, so that I can discover and join projects even if emails were missed.

How it works. Dashboard (OrgHome) → 'Pending invitations' section. Shows invites addressed to logged-in user's email. Each invite displays: project name(s), inviter, role, expiration date (if set). 'Review & accept' link navigates to /join/:token. Section hidden if no pending invites (FRO-326). Fetched server-side via listMyPendingInvites(jwt).

WhoProject lead / owner PermissionsManage members/invites: project_lead(500); role changes: maintainer/owner(≥600); link role capped at contributor(400)
Key files

src/components/org/OrgHome.tsx:172-173,276-284,426-451
src/lib/sync/invites.ts (listMyPendingInvites)

Cross-org shared projects (Shared with you section) ▶ 00:00

As a user invited to a project from an external org, I want to see that project in my dashboard, so that I can access it even though I'm not a member of the org.

How it works. Dashboard → 'Shared with you' section (if user has projects from orgs they don't belong to). Lists projects shared via invite link or bulk-add grants. Each row: project name, role. Clicking navigates to /projects/:id. Section hidden if no shared projects (FRO-335). Fetched via fetchAccessibleProjects(jwt) and partitioned client-side using partitionSharedProjects().

WhoProject lead / owner PermissionsManage members/invites: project_lead(500); role changes: maintainer/owner(≥600); link role capped at contributor(400)
Key files

src/components/org/OrgHome.tsx:169-170,267-274,330,879-898
src/lib/frontier/shared-projects.ts (partitionSharedProjects)

Assigned to me (inbox view) ▶ 00:00

As a translator, I want to see all my open assignments in one place, so that I can quickly find and start work.

How it works. Navigate to /assigned-to-me (via sidebar). Shows assignments across all projects in active org. Each row displays: scope label (e.g., 'GEN 1' or filename), project name, deadline (if set), progress bar (cells done / total), completion %. Clicking row navigates to project editor. Empty state: 'You have no open assignments.' Fetched via getMyAssignmentsForOrg(jwt, orgId) on mount.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/AssignedToMe.tsx:16-102
src/lib/sync/assignments.ts (getMyAssignmentsForOrg)

Project card role badge ▶ 00:00

As a user, I want to see my role in each project, so that I know what permissions I have.

How it works. Dashboard project rows display role badge (small pill) with role name (e.g., 'Reviewer', 'Translator', 'Project Lead'). Badge styling consistent across cards. Role fetched from project's role.name field (from server).

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/OrgHome.tsx:662-666,834-837
src/components/org/ProjectsList.tsx:126-162

Orgs list view (All Organizations) ▶ 00:00

As a user, I want to see all organizations I belong to with their project stats, so that I can quickly compare org productivity.

How it works. OrgHome.tsx when isAllOrgs=true. Grid of org cards showing: org name (with 'Workspace' fallback), role, project count, avg translation %, avg validation %. Summary stats above: total org count, total project count, avg translated/validated/audio %, stalled/overdue counts. Filter box narrows org list by name. Clicking org card navigates to that org's overview (/projects?org=N). Empty state: 'No organizations yet.'

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/OrgHome.tsx:313,332-350,452-536
src/context/OrgContext.tsx (isAllOrgs, setActiveOrg)

All projects list (cross-org view) ▶ 00:00

As a power user, I want to see all projects across all orgs in one view with sorting options, so that I can stay aware of cross-org progress.

How it works. OrgHome.tsx when isAllOrgs=true. Projects section shows all projects from all orgs with view lens buttons (Recently updated / Needs attention / Overdue / Least translated). Each project displays: name, org badge (if in multi-org view), role badge, status, progress bars. Can filter by name, status (All/Stalled/Overdue), and sort lens. org badge shown for all-orgs view but hidden for single-org view.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/OrgHome.tsx:343-702
src/lib/frontier/portfolio.ts (attentionRank for 'Needs attention' lens)

Inactive project indicator ▶ 00:00

As a user, I want to see which projects are inactive/frozen, so that I know they cannot be edited.

How it works. Project overview header displays 'Inactive' badge next to title (if isActive=false). Badge styling: amber background (warning color). Banner appears at top of workspace page (InactiveProjectBanner component) stating project cannot be edited. Reactivate button shown to project_lead+ users. Read-only message shown to non-managers.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:372-379
src/components/InactiveProjectBanner.tsx

Archived project indicator ▶ 00:00

As a user, I want to see which projects are archived, so that I know they are no longer active.

How it works. Project overview header displays 'Archived' badge next to title (if deletedAt is set). Badge styling: muted/secondary. Archived projects not listed on active project list (/projects), but appear on /projects/archived. Open project button and most actions hidden when archived. Only restore action available (to owners).

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:369-371,394-401
src/lib/sync/cloud-projects.ts (minimalProjectRecord sets deletedAt + deletedBy)

Project progress bars (overview detail card) ▶ 00:00

As a manager, I want to see detailed progress metrics (translated cells, validated cells, audio cells) for a project, so that I can understand translation and validation status.

How it works. Project overview → Progress card (shown if project.totalCells > 0). Displays: (1) big-number tiles showing % complete for Translated, AI Drafted (if any), Validated, Audio (if any). Each tile colored (amber/violet/emerald/sky). (2) Detail bars below showing actual cell counts (e.g., '42/100 cells translated'). Metrics sourced from portfolio data (filledCells, validatedCells, aiDraftedCells, audioCells). Tooltips on tiles explain AI Drafted = drafted but not human-edited/validated.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:440-515
src/lib/frontier/portfolio.ts (translatedPct, validatedPct, aiDraftedPct, audioPct)

Per-file progress rows (overview detail card) ▶ 00:00

As a manager, I want to see progress per file/book, so that I can identify files lagging behind others.

How it works. Project overview → Files card. Shows top N files (FILE_ROW_CAP=12) sorted by cell count descending. Each row: filename, mini progress bars (translated, validated), cell counts, word count. '+N more' toggle shows/hides remaining files. Fetched via fetchProjectFiles() on component mount. Tooltips on filename and cell count for clarity.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:232-242,517-574

Overflow menu (project actions) ▶ 00:00

As a manager, I want quick access to project actions (archive, download, lifecycle), so that I don't clutter the main UI.

How it works. Project overview header → '⋯' (three-dot) button. Opens dropdown menu with: 'Download deliverable' (maintainer+), 'Mark as Inactive/Active' (project_lead+), 'Archive' (owner only, shown in danger color). Menu closes on outside click. Buttons disabled if action in progress (busy state) or permission missing.

WhoProject lead / org admin PermissionsView: viewer(100); create/archive/delete project: project_lead(500)+/owner(700)
Key files

src/components/org/ProjectOverview.tsx:135-181,404-432