|
| 1 | +# Multi-Team Support Plan for Pulse App |
| 2 | + |
| 3 | +## Background |
| 4 | +Currently, Pulse is hardcoded to work for a single company (e.g., @company.com emails). To scale, we need to support multiple teams/companies, each with their own users, admins, and settings. This document outlines the requirements, migration plan, and technical considerations for enabling multi-team support. |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +## Goals |
| 9 | +- Allow anyone to create a new team (company/workspace) in Pulse |
| 10 | +- Remove hardcoded email domain restrictions (e.g., NEXT_PUBLIC_COMPANY_EMAIL_DOMAIN) |
| 11 | +- Support team-specific authentication, onboarding, and admin controls |
| 12 | +- Ensure data isolation between teams (submissions, projects, users, etc.) |
| 13 | +- Enable team discovery/joining via invite or request |
| 14 | +- Support both Weekly and Daily Pulse flows per team |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## Requirements |
| 19 | + |
| 20 | +### 1. Team Model |
| 21 | +- Add a `teams` table (id, name, slug, created_at, owner_id, etc.) |
| 22 | +- Each user can belong to one or more teams (many-to-many: `team_members` table) |
| 23 | +- Each submission, project, etc. is linked to a team |
| 24 | + |
| 25 | +### 2. Team Creation & Onboarding |
| 26 | +- New users can create a team on signup |
| 27 | +- Existing users can invite others to their team (via email invite or link) |
| 28 | +- Optionally, allow open registration or require admin approval |
| 29 | + |
| 30 | +### 3. Authentication & Authorization |
| 31 | +- Remove global email domain check |
| 32 | +- On login, users select or are routed to their team |
| 33 | +- Team admins can manage members, roles, and settings |
| 34 | + |
| 35 | +### 4. Data Isolation |
| 36 | +- All queries for submissions, projects, questions, etc. must be filtered by team |
| 37 | +- No cross-team data leakage |
| 38 | + |
| 39 | +### 5. UI/UX Changes |
| 40 | +- Team switcher in the UI (if user belongs to multiple teams) |
| 41 | +- Team-specific branding (logo, name, etc.) |
| 42 | +- Team management screens (admin only) |
| 43 | +- Update onboarding/login flows to support team context |
| 44 | + |
| 45 | +### 6. Migration |
| 46 | +- Migrate existing users and data to a default team (e.g., 'CoderPush') |
| 47 | +- Backfill all existing records with team_id |
| 48 | + |
| 49 | +### 7. API & Backend |
| 50 | +- Update all API endpoints to require and validate team context |
| 51 | +- Enforce team membership/roles in backend logic |
| 52 | + |
| 53 | +### 8. Email & Notifications |
| 54 | +- Team-specific email templates and sender info |
| 55 | +- Invites, reminders, and notifications scoped to team |
| 56 | + |
| 57 | +--- |
| 58 | + |
| 59 | +## Technical Steps |
| 60 | +1. **DB Schema:** |
| 61 | + - Create `teams` and `team_members` tables |
| 62 | + - Add `team_id` to relevant tables (users, submissions, projects, etc.) |
| 63 | +2. **Auth:** |
| 64 | + - Remove domain restriction logic |
| 65 | + - Add team selection/creation to signup/login |
| 66 | +3. **API:** |
| 67 | + - Update endpoints to require team context |
| 68 | + - Enforce team membership/roles |
| 69 | +4. **UI:** |
| 70 | + - Add team switcher and management screens |
| 71 | + - Update onboarding and invite flows |
| 72 | +5. **Migration:** |
| 73 | + - Script to migrate all existing data to a default team |
| 74 | +6. **Testing:** |
| 75 | + - Test for data isolation, team switching, and onboarding |
| 76 | +7. **Docs:** |
| 77 | + - Update documentation for multi-team support |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +## Open Questions |
| 82 | +- Should users be able to belong to multiple teams with the same email? |
| 83 | +- Should teams be discoverable/searchable, or invite-only? |
| 84 | +- What are the default roles/permissions per team? |
| 85 | +- How to handle billing (if/when needed) per team? |
| 86 | + |
| 87 | +--- |
| 88 | + |
| 89 | +## References |
| 90 | +- See `Weekly Pulse - Product Requirements Document (PRD).md` for current flows |
| 91 | +- See `auto-login-plan.md` for authentication details |
| 92 | + |
| 93 | +--- |
| 94 | + |
| 95 | +## Implementation Plan (Phased Approach) |
| 96 | + |
| 97 | +### Phase 1: Core Multi-Team Foundation |
| 98 | +**Database Schema Changes:** |
| 99 | +```sql |
| 100 | +-- New tables |
| 101 | +CREATE TABLE teams ( |
| 102 | + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), |
| 103 | + name TEXT NOT NULL, |
| 104 | + slug TEXT UNIQUE NOT NULL, |
| 105 | + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), |
| 106 | + owner_id UUID REFERENCES users(id) |
| 107 | +); |
| 108 | + |
| 109 | +CREATE TABLE team_members ( |
| 110 | + team_id UUID REFERENCES teams(id) ON DELETE CASCADE, |
| 111 | + user_id UUID REFERENCES users(id) ON DELETE CASCADE, |
| 112 | + role TEXT DEFAULT 'member', -- 'owner', 'admin', 'member' |
| 113 | + joined_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), |
| 114 | + PRIMARY KEY (team_id, user_id) |
| 115 | +); |
| 116 | + |
| 117 | +-- Add team_id to existing tables (nullable initially for safe migration) |
| 118 | +ALTER TABLE users ADD COLUMN current_team_id UUID REFERENCES teams(id); |
| 119 | +ALTER TABLE submissions ADD COLUMN team_id UUID REFERENCES teams(id); |
| 120 | +ALTER TABLE projects ADD COLUMN team_id UUID REFERENCES teams(id); |
| 121 | +ALTER TABLE questions ADD COLUMN team_id UUID REFERENCES teams(id); |
| 122 | +``` |
| 123 | + |
| 124 | +### Phase 2: Data Migration |
| 125 | +**Migration Script:** |
| 126 | +```sql |
| 127 | +-- Create default team for existing data |
| 128 | +INSERT INTO teams (name, slug, owner_id) |
| 129 | +VALUES ('CoderPush', 'coderpush', (SELECT id FROM users WHERE is_admin = true LIMIT 1)); |
| 130 | + |
| 131 | +-- Migrate all existing users to default team |
| 132 | +INSERT INTO team_members (team_id, user_id, role) |
| 133 | +SELECT t.id, u.id, CASE WHEN u.is_admin THEN 'admin' ELSE 'member' END |
| 134 | +FROM teams t, users u WHERE t.slug = 'coderpush'; |
| 135 | + |
| 136 | +-- Backfill team_id for all existing data |
| 137 | +UPDATE submissions SET team_id = (SELECT id FROM teams WHERE slug = 'coderpush'); |
| 138 | +UPDATE projects SET team_id = (SELECT id FROM teams WHERE slug = 'coderpush'); |
| 139 | +UPDATE questions SET team_id = (SELECT id FROM teams WHERE slug = 'coderpush'); |
| 140 | +UPDATE users SET current_team_id = (SELECT id FROM teams WHERE slug = 'coderpush'); |
| 141 | + |
| 142 | +-- Make team_id NOT NULL after migration |
| 143 | +ALTER TABLE submissions ALTER COLUMN team_id SET NOT NULL; |
| 144 | +ALTER TABLE projects ALTER COLUMN team_id SET NOT NULL; |
| 145 | +ALTER TABLE questions ALTER COLUMN team_id SET NOT NULL; |
| 146 | +``` |
| 147 | + |
| 148 | +### Phase 3: Authentication & API Updates |
| 149 | +- Remove domain restriction from `src/app/auth/callback/route.ts` |
| 150 | +- Add team context to all API endpoints in `/src/app/api/` |
| 151 | +- Update auto-sharing logic to work within team boundaries |
| 152 | +- Add team selection/creation to signup flow |
| 153 | + |
| 154 | +**Key Files to Update:** |
| 155 | +- `src/utils/companyDomain.ts` - Remove hardcoded domain logic |
| 156 | +- `src/app/auth/callback/route.ts` - Remove domain validation (lines 20-27) |
| 157 | +- `src/app/api/submissions/route.ts` - Add team filtering and update auto-sharing (lines 122-143) |
| 158 | +- `src/app/api/admin/submissions/[id]/share/route.ts` - Update sharing restrictions (lines 66-73) |
| 159 | + |
| 160 | +### Phase 4: UI & Team Management |
| 161 | +- Team switcher component |
| 162 | +- Team creation/invitation flows |
| 163 | +- Admin team management interface |
| 164 | +- Update all queries to filter by current team |
| 165 | +- Add Row Level Security (RLS) policies for data isolation |
| 166 | + |
| 167 | +**Data Isolation with RLS:** |
| 168 | +```sql |
| 169 | +-- Enable RLS on all team-scoped tables |
| 170 | +ALTER TABLE submissions ENABLE ROW LEVEL SECURITY; |
| 171 | +ALTER TABLE projects ENABLE ROW LEVEL SECURITY; |
| 172 | +ALTER TABLE questions ENABLE ROW LEVEL SECURITY; |
| 173 | + |
| 174 | +-- Create policies for team isolation |
| 175 | +CREATE POLICY team_isolation_submissions ON submissions |
| 176 | +FOR ALL USING (team_id IN ( |
| 177 | + SELECT team_id FROM team_members WHERE user_id = auth.uid() |
| 178 | +)); |
| 179 | +``` |
| 180 | + |
| 181 | +## Critical Migration Considerations |
| 182 | + |
| 183 | +### Schema Conflicts |
| 184 | +- Current `submissions` table has NOT NULL constraints that need to be handled carefully during migration |
| 185 | +- Need to coordinate with daily/weekly reporting refactor if implementing simultaneously |
| 186 | + |
| 187 | +### Data Safety |
| 188 | +1. Test migration with production data copy first |
| 189 | +2. Use nullable team_id initially, then make NOT NULL after backfill |
| 190 | +3. Implement RLS policies for data isolation |
| 191 | +4. Add monitoring for cross-team data leaks |
| 192 | + |
| 193 | +### Performance |
| 194 | +- Add indexes on team_id columns after migration |
| 195 | +- Consider partitioning large tables by team_id for better performance |
| 196 | + |
| 197 | +## Next Steps |
| 198 | +- ✅ Schema design validated against current database |
| 199 | +- ✅ Migration risks identified and mitigated |
| 200 | +- ✅ Phased implementation plan created |
| 201 | +- 🔄 Ready for implementation in phases |
0 commit comments