Excluded: .git, node_modules, secrets/, compose.env, assemblyscript tgz Source: /opt/alga-psa on psa.joliet.tech
13 KiB
PRD — Teams V2: Organizational Hierarchy + Operational Teams
- Slug:
teams-v2 - Date:
2026-02-26 - Status: Draft
Summary
Split the existing single "teams" concept into two distinct structures:
- "Reports To" (Organizational Hierarchy) — A
reports_tofield on each user forming a manager-subordinate tree. Drives org chart display, timesheet approval chains, and scheduling scope. - "Teams" (Functional/Operational Groups) — Repurpose existing teams as cross-functional groups that can be assigned to tickets and project tasks as a unit. Team lead auto-becomes primary assignee. Members are expanded into
ticket_resources.
All new UI elements are gated behind a teams-v2 PostHog feature flag. Schema migrations are additive and not gated.
Problem
The current team system is a settings-only feature disconnected from day-to-day workflows:
- Teams cannot be assigned to tickets or tasks — work assignment is individual-only
- Timesheet approval is tightly coupled to team manager, but there's no formal org hierarchy — if a user's manager changes, you have to restructure teams
- There's no org chart visualization
- Manager-of-manager cannot approve subordinates' timesheets (no transitive authority)
- Several API endpoints reference non-existent database tables and crash at runtime
Goals
- Introduce a
reports_torelationship on users to model organizational hierarchy - Auto-seed
reports_tofrom existing team/manager data (zero customer effort) - Enable timesheet approval via the
reports_tochain (transitive — manager of manager can approve) - Provide org chart visualization in User Management
- Enable assigning an entire team to a ticket or project task
- When a team is assigned, auto-set team lead as primary assignee and expand members into ticket resources
- Provide a new
UserAndTeamPickercomponent that shows both users and teams - Show team badge on the "Agent team" card in ticket detail
- Enhance the "Assigned To" ticket list filter to include teams
- Gate all new UI behind a feature flag for safe, gradual rollout
Non-goals
- Team logos or custom team icons (use generic team icon only)
- Team-based reporting or analytics dashboards
- Team notifications on assignment
- Team-based dispatch filtering
- Workflow automation with team routing rules
- "My Team" ticket queue view
- Cleaning up broken API endpoints (deferred to Phase 3)
- Removing the existing team-manager approval path (kept for backwards compatibility)
Users and Primary Flows
Personas
- MSP Admin — Sets up org structure and teams. Manages user profiles.
- Team Manager / Lead — Approves timesheets for direct reports. Leads a functional team.
- Technician / Agent — Works on assigned tickets. May belong to one or more teams.
Primary Flows
Flow 1: Admin sets up "Reports To" hierarchy
- Admin opens User Management
- Edits a user profile
- Sets the "Reports To" dropdown to select their manager
- Views the org chart tab to verify the hierarchy looks correct
Flow 2: Manager approves timesheets via reporting chain
- Manager opens timesheet approval dashboard
- Sees timesheets from all direct and indirect reports (via
reports_tochain) - Approves or requests changes as usual
Flow 3: Agent assigns a team to a ticket
- Agent opens a ticket
- In the "Assigned To" field, the
UserAndTeamPickershows users and teams - Agent selects "Network Team"
- System sets
assigned_team_id, makes team lead the primary assignee (if empty), and adds all other members as additional agents - Team badge appears on the "Agent team" card
Flow 4: Agent removes a team from a ticket
- Agent clicks "x" on the team badge
- Confirmation dialog appears with options:
- Remove all team members from assignment
- Keep all team members as individual agents
- Select individual members to keep/remove
- Agent makes selection,
assigned_team_idis cleared
Flow 5: Filtering tickets by team
- Agent opens ticket list
- Uses the "Assigned To" filter which now shows teams alongside users
- Selects a team to see all tickets assigned to that team
UX / UI Notes
Reports To
- "Reports To" dropdown on the user edit form — standard dropdown selecting from all users in the tenant
- Org chart tab in User Management — tree view visualization of reporting lines (read-only)
Team Assignment
-
UserAndTeamPicker— new component replacingUserPickervia feature flag swap:{featureFlag('teams-v2') ? <UserAndTeamPicker ... /> : <UserPicker ... />}- Users section first, teams section below with separator
- Teams show member count and lead name
- Generic team icon for teams vs. user avatar for individuals
-
Team badge on "Agent team" card — chip/badge following existing Additional Agents pattern (initials/icon + name + x button). Uses same chip styling as the existing
MultiUserPickerchips (thebg-gray-100 rounded-fullpill pattern). -
Team removal dialog — confirmation dialog with radio buttons:
- "Remove all team members"
- "Keep all team members as individual agents"
- Individual checkboxes for each team member
-
"Assigned To" filter enhancement — existing
MultiUserPickerfilter inTicketingDashboard.tsxenhanced to show teams in the dropdown alongside users when flag is on. -
All new UI must follow established component variants, color schemes, and interaction patterns.
Requirements
Functional Requirements
Phase 1: Reports To + Org Hierarchy
- FR-1.1: Add nullable
reports_to UUIDcolumn touserstable referencingusers(user_id) - FR-1.2: Auto-seed
reports_tofrom existing team membership data in the migration (team member → team manager_id) - FR-1.3: Validate that setting
reports_todoesn't create circular chains - FR-1.4: Add "Reports To" dropdown field on user edit form (behind feature flag)
- FR-1.5: Build org chart tree view in User Management (behind feature flag)
- FR-1.6: Add
isInReportsToChain()function for transitive approval check - FR-1.7: Extend
canApprovelogic: existing team-manager check (always active) +reports_tochain check (behind feature flag) - FR-1.8: Update
fetchTimeSheetsForApproval()to include timesheets fromreports_tosubordinates when flag is on - FR-1.9: Update AvailabilitySettings to additionally scope by
reports_towhen flag is on - FR-1.10: Update SchedulePage to additionally scope by
reports_towhen flag is on
Phase 2: Operational Teams
- FR-2.1: Add
role TEXT DEFAULT 'member'column toteam_memberstable - FR-2.2: Migration: set
role = 'lead'for existing team members whoseuser_idmatchesteams.manager_id - FR-2.3: Add nullable
assigned_team_id UUIDcolumn toticketstable referencingteams(team_id) - FR-2.4: Add nullable
assigned_team_id UUIDcolumn toproject_taskstable referencingteams(team_id) - FR-2.5: Implement team assignment action: sets
assigned_team_id, assigns lead as primary (ifassigned_tois NULL), expands other members intoticket_resourceswithrole = 'team_member' - FR-2.6: When assigning team and
assigned_tois already set: add all team members as resources EXCEPT whoever is alreadyassigned_to - FR-2.7: Team lead must NOT be added as
ticket_resourceswhen they are the primary assignee (DB constraint:assigned_to != additional_user_id) - FR-2.8: Build
UserAndTeamPickercomponent showing users and teams in grouped sections (behind feature flag) - FR-2.9: Feature flag swap: render
UserAndTeamPickerinstead ofUserPickeron ticket/task detail whenteams-v2is enabled - FR-2.10: Show team badge (chip: generic icon + team name + x) on "Agent team" card when
assigned_team_idis set (behind feature flag) - FR-2.11: Clicking "x" on team badge shows confirmation dialog with removal options
- FR-2.12: "Remove all" option removes
ticket_resourceswithrole = 'team_member'and clearsassigned_team_id - FR-2.13: "Keep all" option clears
assigned_team_idonly, resources stay - FR-2.14: Individual selection option lets user choose which team members to keep/remove
- FR-2.15: Primary
assigned_tois never automatically removed when clearing team assignment - FR-2.16: Adding/removing individual agents does NOT affect team assignment. Team badge and individual agent chips coexist independently.
- FR-2.17: Team membership is a snapshot at assignment time — later team roster changes don't affect existing ticket resources
- FR-2.18: Enhance "Assigned To" filter in ticket list to show teams alongside users when flag is on
- FR-2.19: Selecting a team in the filter → filters by
tickets.assigned_team_id - FR-2.20: Extend
tickets.createworkflow action to supportassignee: { type: 'team', id: uuid }(matchingprojects.create_taskpattern) - FR-2.21: Update
ITicketandIProjectTaskinterfaces to includeassigned_team_id - FR-2.22: Update tenant export to include new columns (
reports_to,assigned_team_id,role)
Non-functional Requirements
- NFR-1: All new UI elements gated behind
teams-v2PostHog feature flag - NFR-2: Schema migrations are additive (nullable columns) and not feature-flag-gated
- NFR-3: Existing team-manager timesheet approval logic remains active and unchanged
- NFR-4: Multi-tenant isolation preserved — all queries tenant-scoped
- NFR-5:
isInReportsToChain()must handle arbitrarily deep hierarchies without stack overflow (use iterative/CTE approach)
Data / API / Integrations
Schema Changes
-- Phase 1
ALTER TABLE users ADD COLUMN reports_to UUID REFERENCES users(user_id);
-- Auto-seed
UPDATE users u SET reports_to = t.manager_id
FROM team_members tm
JOIN teams t ON t.team_id = tm.team_id AND t.tenant = tm.tenant
WHERE u.user_id = tm.user_id AND u.tenant = tm.tenant AND u.user_id != t.manager_id;
-- Phase 2
ALTER TABLE team_members ADD COLUMN role TEXT NOT NULL DEFAULT 'member';
UPDATE team_members tm SET role = 'lead'
FROM teams t WHERE tm.team_id = t.team_id AND tm.tenant = t.tenant AND tm.user_id = t.manager_id;
ALTER TABLE tickets ADD COLUMN assigned_team_id UUID REFERENCES teams(team_id);
ALTER TABLE project_tasks ADD COLUMN assigned_team_id UUID REFERENCES teams(team_id);
Affected Interfaces
IUser— addreports_to?: stringITeam— existing, no change needed (manager_id stays as team lead reference)ITeamMember— addrole: 'member' | 'lead'ITicket— addassigned_team_id?: stringIProjectTask— addassigned_team_id?: stringITicketResource—rolefield will use'team_member'value for team-assigned resources
API Considerations
- Existing team CRUD endpoints unchanged
- New server actions needed:
assignTeamToTicket(),removeTeamFromTicket(),getReportsToChain() - Existing
addTicketResource()reused for individual member expansion
Security / Permissions
reports_tochanges: require user edit permission- Team assignment on tickets: require ticket edit permission (same as changing
assigned_to) - Timesheet approval via
reports_to: requiretimesheet:approvepermission (existing) - Cycle prevention on
reports_to: server-side validation, not client-only - All queries continue to be tenant-scoped via RLS policies
Rollout / Migration
Migration Safety
- All schema changes are additive nullable columns — no data loss, no breaking changes
reports_toauto-seeded in migration from existing team data (verified safe for all active tenants)- All UI behind feature flag — invisible until enabled
- Existing approval logic untouched — works in parallel
Rollout Plan
- Deploy migrations (schema + data seed) — invisible to users
- Enable
teams-v2flag on staging for internal testing - Enable flag on Nine Minds tenant for dogfooding
- Gradual per-tenant rollout to customers
- Once stable, remove flag gates and make generally available
Open Questions
None — all questions resolved during analysis phase.
Acceptance Criteria (Definition of Done)
reports_tocolumn exists on users and is auto-seeded from existing team data- Setting
reports_torejects circular chains - Org chart view displays the reporting hierarchy correctly
- Managers can approve timesheets for direct and indirect reports via
reports_tochain - A team can be assigned to a ticket, setting team lead as primary and expanding members into resources
- Team badge appears on "Agent team" card with working "x" removal
- Team removal dialog offers all three options and executes correctly
UserAndTeamPickershows users and teams, swaps correctly via feature flag- "Assigned To" filter includes teams when flag is on
- All new UI is invisible when
teams-v2flag is off - Existing team-manager approval continues to work unchanged
- No regressions in existing ticket assignment, resource management, or timesheet approval