# Product Requirements Document: Manage Team Members

**Specification ID:** 014-manage-employees-unified
**Version:** 1.0
**Status:** DRAFT
**Last Updated:** December 2025
**Depends On:** 007-unified-users-auth (implemented)

---

## Validation Checklist

- [ ] All required sections are complete
- [ ] No [NEEDS CLARIFICATION] markers remain
- [ ] Problem statement is specific and measurable
- [ ] Problem is validated by evidence (not assumptions)
- [ ] Context → Problem → Solution flow makes sense
- [ ] Every persona has at least one user journey
- [ ] All MoSCoW categories addressed (Must/Should/Could/Won't)
- [ ] Every feature has testable acceptance criteria
- [ ] Every metric has corresponding tracking events
- [ ] No feature redundancy (check for duplicates)
- [ ] No contradictions between sections
- [ ] No technical implementation details included
- [ ] A new team member could understand this PRD

---

## Product Overview

### Vision
A unified, modern "Manage Team Members" page that provides store managers with a single, intuitive interface to manage all staff—whether they have system login access or not—built on the new unified user architecture.

### Problem Statement
Store managers currently face a fragmented, confusing employee management experience:

1. **Dual-System Confusion**: The page operates across TWO parallel systems (legacy per-store `employees` table AND the new central `users` table), creating data inconsistencies and unclear source-of-truth
2. **Complex Promotion Workflow**: Converting an employee to a user with login access requires navigating multiple modals and understanding the "user-employee linking" concept that should be invisible
3. **Disorganized UI**: The current page mixes Bootstrap 3 and 5 classes, has multiple overlapping modals, and presents an overwhelming amount of information without clear hierarchy
4. **PIN Management Split**: Clock PINs are stored in different locations depending on system flags, causing sync issues for time punch verification
5. **No Audit Trail**: Managers cannot see who made changes to employee records or when

**Business Impact**: Store managers spend extra time managing employees, make errors due to UI confusion, and lack confidence in data accuracy.

### Value Proposition
The new "Manage Team Members" page delivers:
- **Single Source of Truth**: All team member data lives in the unified users system—no more dual-database confusion
- **Simplified Access Control**: A simple toggle to grant/revoke system login access instead of complex "promotion" workflows
- **Modern, Clean UI**: Full Bootstrap 5 redesign with clear visual hierarchy and intuitive navigation
- **PIN-First for External Staff**: External provider employees get BuyerKiosk clock PINs without requiring full account setup
- **Audit Visibility**: Clear history of who changed what and when

## User Personas

### Primary Persona: Store Manager (Sarah)
- **Demographics:** 35-50 years old, manages daily operations at one pawn shop location, moderate technical comfort (uses scheduling apps, POS systems)
- **Goals:**
  - Quickly onboard new hires so they can clock in and work
  - Grant/revoke system access without IT involvement
  - Keep employee info up-to-date (contact info, emergency contacts)
  - Track who's active, on leave, or terminated
- **Pain Points:**
  - Current page is confusing—"What's the difference between an employee and a user?"
  - Multiple modals for different actions feels overwhelming
  - Can't tell if WhenIWork sync actually worked
  - Setting up clock PINs is buried in the interface

### Secondary Persona: Store Owner (Mike)
- **Demographics:** 45-60 years old, owns 2-3 store locations, delegates most operations but needs visibility
- **Goals:**
  - See all staff across all stores from one view
  - Understand who has system access and at what level
  - Ensure security (know who can access what)
- **Pain Points:**
  - Has to switch between stores to see full team picture
  - Unclear which employees can log into the system
  - No visibility into when employee records were changed

### Secondary Persona: New Employee (Alex)
- **Demographics:** 22-35 years old, just hired, varying technical comfort
- **Goals:**
  - Get set up quickly so they can start working
  - Clock in/out easily with their PIN
  - Access system if needed for their role (daybook, queue, etc.)
- **Pain Points:**
  - Invitation email workflow is confusing
  - Doesn't understand why they need two different credentials (external scheduler + BuyerKiosk)

## User Journey Maps

### Primary User Journey: Onboarding a New Hire (WhenIWork Store)

**Context:** Sarah (Store Manager) has a new employee starting tomorrow who was added to WhenIWork by the scheduling manager.

| Step | Current Experience | Target Experience |
|------|-------------------|-------------------|
| 1. **Sync** | Click "Sync Now", wait, hope it worked. Check if employee appeared. | Click "Sync", see real-time progress, confirmation message shows "1 new team member added" |
| 2. **Find** | Scroll through unsorted list looking for new name | New members highlighted at top, or use search/filter |
| 3. **Set PIN** | Click employee row → Edit modal → scroll to find PIN field → save | Click PIN icon directly on row → enter 4-6 digits → done |
| 4. **Verify** | Check that green PIN indicator appeared on row | Clear "PIN set" badge on row, confirmation toast |
| 5. **Grant Access** (optional) | Navigate to "Unlinked Employees" panel → "Set Up Account" modal → fill username/password → submit → separate PIN modal | Click "Grant Access" toggle on row → choose invite or admin-create → single flow |

### Secondary Journey: Granting System Access via Invitation

**Context:** Sarah wants Alex (new employee) to log into BuyerKiosk to manage the buy queue.

| Step | Action |
|------|--------|
| 1. | Open Team Members page |
| 2. | Find Alex in the list |
| 3. | Toggle "Can Login" switch ON |
| 4. | System prompts: "How should Alex set up their account?" |
| 5. | Sarah selects "Send Invitation Email" |
| 6. | System sends email with registration link |
| 7. | Alex clicks link, creates password, optionally enables MFA |
| 8. | Alex can now log in |

### Secondary Journey: Admin-Created Account

**Context:** Sarah needs to create credentials for an employee without email access.

| Step | Action |
|------|--------|
| 1. | Find employee in Team Members list |
| 2. | Toggle "Can Login" switch ON |
| 3. | Select "Create Credentials Now" |
| 4. | Enter username and password (or generate) |
| 5. | Optionally set clock PIN at same time |
| 6. | Save—employee can now log in with provided credentials |

### Secondary Journey: Removing System Access

**Context:** An employee is leaving or no longer needs system access.

| Step | Action |
|------|--------|
| 1. | Find employee in Team Members list |
| 2. | Toggle "Can Login" switch OFF |
| 3. | Confirm: "Remove login access? They will retain their clock PIN." |
| 4. | Employee can no longer log in but can still clock in/out |

## Feature Requirements

### Must Have Features

#### Feature 1: Unified Team Member List
- **User Story:** As a store manager, I want to see all team members (employees and users) in one list so that I don't have to understand the underlying system architecture.
- **Acceptance Criteria:**
  - [ ] Page displays all users with store assignments for current store
  - [ ] Shows both users with login access (`canLogin=true`) and employees without (`canLogin=false`)
  - [ ] Each row displays: photo, name, position, role badge, contact info, status, login access indicator, PIN status
  - [ ] Supports sorting by name, position, status, date added
  - [ ] Supports search/filter by name, email, position
  - [ ] Paginated list for stores with many team members (25 per page default)

#### Feature 2: Clock PIN Management
- **User Story:** As a store manager, I want to set/change a team member's clock PIN so that I can quickly enable time clock access.
- **Acceptance Criteria:**
  - [ ] PIN icon visible on each row indicates PIN status (set vs not set)
  - [ ] PIN entry accessible via detail modal's "Access & Security" tab (per ADR-4)
  - [ ] Validates PIN format: 4-6 digits only
  - [ ] Success/error feedback via toast notification
  - [ ] Option to remove PIN
  - [ ] Works for ALL team members, even those without login access
- **Note:** Per ADR-4, PIN management is in the detail modal for simpler table UI (not inline)

#### Feature 3: Login Access Toggle
- **User Story:** As a store manager, I want to toggle login access on/off for team members so that I can control who can log into BuyerKiosk.
- **Acceptance Criteria:**
  - [ ] "Can Login" toggle switch visible on each row
  - [ ] Toggling ON prompts for account setup method (invite or admin-create)
  - [ ] Toggling OFF shows confirmation dialog
  - [ ] Disabling login does NOT remove clock PIN
  - [ ] Disabling login does NOT delete the user—just sets `canLogin=false`
  - [ ] Clear visual distinction between login-enabled and login-disabled members

#### Feature 4: Account Setup Flow (Invite or Admin-Create)
- **User Story:** As a store manager, I want to grant system access via email invitation OR by creating credentials directly so that I have flexibility based on the situation.
- **Acceptance Criteria:**
  - [ ] When enabling login access, prompt with two options: "Send Invitation" or "Create Credentials"
  - [ ] **Invite flow:** Enter/confirm email → system sends invitation → employee self-registers
  - [ ] **Admin-create flow:** Enter username, generate/enter password, optionally set PIN → save
  - [ ] Both flows result in `canLogin=true` and appropriate audit log entry
  - [ ] Invitation tokens expire after 7 days
  - [ ] Show pending invitation status for invited users

#### Feature 5: External Provider Sync (WhenIWork)
- **User Story:** As a store manager, I want to sync team members from WhenIWork so that new hires from the scheduling system appear in BuyerKiosk.
- **Acceptance Criteria:**
  - [ ] "Sync from WhenIWork" button visible when store uses WhenIWork
  - [ ] Progress indicator during sync
  - [ ] Summary after sync: "Added X, Updated Y, Deactivated Z"
  - [ ] Newly synced members appear at top of list (or highlighted)
  - [ ] Synced members display WhenIWork badge
  - [ ] External fields (name, email from WhenIWork) marked as read-only
  - [ ] Local fields (PIN, emergency contact, role assignment) remain editable

#### Feature 6: Team Member Detail/Edit
- **User Story:** As a store manager, I want to view and edit team member details so that I can keep information up to date.
- **Acceptance Criteria:**
  - [ ] Click row to open detail slide-out panel (not modal)
  - [ ] View all member info: contact, employment dates, emergency contact, role, position
  - [ ] Edit mode for editable fields
  - [ ] Photo upload/management
  - [ ] For external provider members: read-only indicator on synced fields
  - [ ] Save with validation feedback
  - [ ] Cancel returns to list without changes

#### Feature 7: Add Team Member (Homegrown Stores)
- **User Story:** As a store manager at a store without external scheduling, I want to manually add team members so that I can manage my team.
- **Acceptance Criteria:**
  - [ ] "Add Team Member" button (only for homegrown source stores)
  - [ ] Form fields: first name*, last name*, email, phone, position, role, emergency contact
  - [ ] Option to set clock PIN during creation
  - [ ] Option to enable login access during creation
  - [ ] Validation: required fields, email format, PIN format
  - [ ] Success creates user record with store assignment

#### Feature 8: Deactivate Team Member
- **User Story:** As a store manager, I want to deactivate team members who leave so that they no longer appear in active lists.
- **Acceptance Criteria:**
  - [ ] Deactivate action available per row
  - [ ] Confirmation dialog: "Deactivate [Name]? They will no longer appear in active team lists."
  - [ ] Deactivation sets `isActive=false` on store assignment (not delete)
  - [ ] Deactivated members hidden by default, viewable via filter
  - [ ] Option to reactivate deactivated members

### Should Have Features

#### Feature 9: Activity/Audit Log
- **User Story:** As a store owner, I want to see who made changes to team member records so that I have accountability.
- **Acceptance Criteria:**
  - [ ] "Activity" section in team member detail panel
  - [ ] Shows recent changes: what changed, who changed it, when
  - [ ] Events logged: created, updated, PIN changed, login enabled/disabled, deactivated

#### Feature 10: Bulk Actions
- **User Story:** As a store manager, I want to perform actions on multiple team members at once so that I can work more efficiently.
- **Acceptance Criteria:**
  - [ ] Checkbox selection on rows
  - [ ] Bulk actions: Set PIN, Deactivate, Send Invitation
  - [ ] Confirmation dialog shows count of affected members
  - [ ] Progress indicator for bulk operations

#### Feature 11: Filter and View Options
- **User Story:** As a store manager, I want to filter the team list so that I can find specific people quickly.
- **Acceptance Criteria:**
  - [ ] Filter by: status (active/inactive/on leave), login access (yes/no), source (manual/WhenIWork), role
  - [ ] Quick filters as chips above table
  - [ ] Active filter state visible
  - [ ] Clear all filters option

### Could Have Features

#### Feature 12: Multi-Store View (Owner Only)
- **User Story:** As a store owner, I want to see team members across all my stores so that I have a complete picture.
- **Acceptance Criteria:**
  - [ ] Toggle to show "All Stores" vs current store
  - [ ] Store column shows which store(s) each member belongs to
  - [ ] Can filter by specific store

#### Feature 13: Role Color Customization
- **User Story:** As a store manager, I want to customize role badge colors so that they match our team structure.
- **Acceptance Criteria:**
  - [ ] Color picker for each role
  - [ ] Preview of badge appearance
  - [ ] Store-level setting (not per-member)

#### Feature 14: Export Team List
- **User Story:** As a store manager, I want to export the team list so that I can share it or use in other tools.
- **Acceptance Criteria:**
  - [ ] Export to CSV button
  - [ ] Exports visible columns with current filters applied
  - [ ] Excludes sensitive data (PINs, passwords)

### Won't Have (This Phase)

| Exclusion | Rationale |
|-----------|-----------|
| **Payroll/Wage Management** | Out of scope—handled by external systems |
| **Schedule Management** | Remains in WhenIWork/Homebase |
| **Performance Reviews** | Not part of employee management |
| **Homebase Integration** | WhenIWork only for now—Homebase is stub/future |
| **Permissions/Group Management** | Separate admin function, not part of this page |
| **Password Reset by Manager** | Security concern—users should reset their own |
| **Biometric PIN Alternatives** | Future consideration |

## Detailed Feature Specifications

### Feature: Login Access Toggle with Account Setup

**Description:** The most complex feature is the login access toggle because it branches into two different flows (invite vs admin-create) and must handle various states (no account, pending invitation, active account).

**User Flow:**

```
┌─────────────────────────────────────────────────────────────────────┐
│                    LOGIN ACCESS TOGGLE FLOW                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  User clicks "Can Login" toggle ON                                  │
│       │                                                             │
│       ▼                                                             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  Check: Does team member have email?                        │   │
│  │  ├── YES → Show account setup options                       │   │
│  │  └── NO → Force admin-create flow (cannot send invite)      │   │
│  └─────────────────────────────────────────────────────────────┘   │
│       │                                                             │
│       ▼                                                             │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  "How should [Name] set up their account?"                  │   │
│  │                                                              │   │
│  │  ○ Send Email Invitation                                    │   │
│  │    "They'll receive an email to create their password"      │   │
│  │                                                              │   │
│  │  ○ Create Credentials Now                                   │   │
│  │    "You'll set their username and password"                 │   │
│  └─────────────────────────────────────────────────────────────┘   │
│       │                           │                                 │
│       ▼                           ▼                                 │
│  INVITE FLOW                 ADMIN-CREATE FLOW                     │
│  ┌─────────────┐             ┌─────────────────────────┐           │
│  │ Confirm     │             │ Enter:                  │           │
│  │ email addr  │             │ - Username              │           │
│  │             │             │ - Password (or generate)│           │
│  │ [Send]      │             │ - Clock PIN (optional)  │           │
│  └─────────────┘             │                         │           │
│       │                      │ [Create Account]        │           │
│       ▼                      └─────────────────────────┘           │
│  System sends                       │                               │
│  invitation email                   ▼                               │
│       │                      System creates user                    │
│       ▼                      with canLogin=true                     │
│  Toggle shows                       │                               │
│  "Pending" state                    ▼                               │
│       │                      Toggle shows ON                        │
│       │                      User can log in immediately            │
│       │                             │                               │
│       └─────────────┬───────────────┘                               │
│                     ▼                                               │
│              Audit log entry created                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
```

**Business Rules:**

| Rule | Specification |
|------|--------------|
| BR-1 | A team member MUST have a valid email to receive an invitation |
| BR-2 | If no email exists, only admin-create flow is available |
| BR-3 | Username must be unique across the entire system |
| BR-4 | Invitation tokens expire after 7 days |
| BR-5 | Only ONE pending invitation per user at a time (new invite replaces old) |
| BR-6 | Disabling login access does NOT revoke existing sessions (they expire naturally) |
| BR-7 | Disabling login does NOT affect clock PIN or store assignment |
| BR-8 | A user with `canLogin=false` cannot authenticate via any method (session, JWT, API key) |
| BR-9 | Password must meet strength requirements: 8+ characters |
| BR-10 | Admin-created accounts are immediately active (no email verification) |

**Edge Cases:**

| Scenario | Expected Behavior |
|----------|-------------------|
| Toggle ON for user with pending invitation | Show "Resend invitation?" option |
| Toggle ON for user who already has login | No-op, toggle already reflects state |
| Toggle OFF for user with active sessions | Confirm dialog, sessions expire naturally |
| Email already used by another user | Error: "This email is already associated with another account" |
| Username already taken | Error: "Username not available" with suggestions |
| WhenIWork syncs after login disabled | Sync updates profile but does NOT re-enable login |
| User accepts invitation after admin revoked login | Invitation becomes invalid |

### Feature: External Provider Sync (WhenIWork)

**Description:** Synchronization from WhenIWork must create/update user records in the unified system while preserving BuyerKiosk-specific data like clock PINs.

**User Flow:**
1. Manager clicks "Sync from WhenIWork"
2. System fetches employees from WhenIWork API
3. Progress indicator shows sync status
4. For each WhenIWork employee:
   - Check if user exists (by externalId or email match)
   - Create or update user record
   - Create or update store assignment
5. Display summary: "Added 2, Updated 5, Deactivated 1"
6. New members highlighted in list

**Business Rules:**

| Rule | Specification |
|------|--------------|
| SR-1 | Sync matches by `externalId` first, then by email |
| SR-2 | Synced fields (name, email, phone, photo) overwrite local data UNLESS `avatarOverride=true` for photos |
| SR-3 | Local-only fields (clockPin, emergencyContact, roleColor, drsEmployeeId) are NEVER overwritten by sync |
| SR-4 | New synced employees have `canLogin=false` and `source='wheniwork'` |
| SR-5 | Employees removed from WhenIWork get `isActive=false` on next sync (soft deactivate) |
| SR-6 | Sync creates audit log entry with counts |
| SR-7 | If WhenIWork API fails, show error and preserve all existing data |

**Edge Cases:**

| Scenario | Expected Behavior |
|----------|-------------------|
| Same person exists in WhenIWork AND as manual entry | Match by email, convert to WhenIWork source, preserve local data |
| WhenIWork returns duplicate emails | Process first, skip duplicates with warning |
| Network error during sync | Rollback partial changes, show retry option |
| Employee removed from WhenIWork but has BuyerKiosk login | Deactivate store assignment, preserve user account and login access |

## Success Metrics

### Key Performance Indicators

| Metric | Current Baseline | Target | Measurement Method |
|--------|-----------------|--------|-------------------|
| **Time to onboard new hire** | ~5 minutes (sync + multiple modals) | < 2 minutes (sync + inline PIN) | Measured via audit logs |
| **PIN setup completion rate** | Unknown (no tracking) | 95% of active members have PINs | Query store assignments |
| **Page error rate** | Unknown | < 1% of page loads | Frontend error tracking |
| **Support tickets for employee management** | Baseline TBD | 50% reduction | Support ticket tagging |
| **User satisfaction** | No baseline | 4.0+ rating | Optional in-app feedback |

### Tracking Requirements

| Event | Properties | Purpose |
|-------|------------|---------|
| `team_member_page_viewed` | `typeNum`, `userId`, `memberCount` | Track page adoption |
| `team_member_created` | `typeNum`, `source`, `hasEmail`, `hasPin` | Track creation patterns |
| `team_member_pin_set` | `typeNum`, `userId`, `hadPinBefore` | Track PIN adoption |
| `login_access_enabled` | `typeNum`, `userId`, `method` (invite/admin) | Track access grant patterns |
| `login_access_disabled` | `typeNum`, `userId` | Track access revocation |
| `external_sync_completed` | `typeNum`, `provider`, `added`, `updated`, `deactivated`, `errors` | Track sync health |
| `invitation_sent` | `typeNum`, `userId` | Track invite usage |
| `invitation_accepted` | `typeNum`, `userId`, `daysToAccept` | Track invite completion |
| `team_member_deactivated` | `typeNum`, `userId`, `hadLogin` | Track offboarding |

---

## Constraints and Assumptions

### Constraints

| Constraint | Impact |
|------------|--------|
| **Must use unified users table** | All data must go to central `users` + `userStoreAssignments` tables from spec 007 |
| **Backward compatibility** | Existing API endpoints (`/:typeNum/api/employees/*`) must continue working during transition |
| **Bootstrap 5 only** | No Bootstrap 3 classes—full migration to design system |
| **WhenIWork API limits** | Sync frequency limited by WhenIWork API rate limits |
| **No breaking changes to mobile app** | Mobile API must return compatible response shapes |
| **Permission model unchanged** | Must use existing `uri_employees` permission hook |

### Assumptions

| Assumption | Risk if Invalid |
|------------|-----------------|
| Spec 007 unified users migration is complete | Would require dual-write logic |
| Stores using WhenIWork have valid API credentials configured | Sync would fail—need error handling |
| Users have modern browsers (Chrome, Safari, Firefox, Edge) | Some Bootstrap 5 features may not work |
| Store managers have email access to send invitations | May need to use admin-create more often |
| Clock PINs are stored in `userStoreAssignments.clockPin` | May need data migration if not |

## Risks and Mitigations

| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| **Data migration issues** from legacy employees table | High | Medium | Run migration in parallel, verify counts match, keep rollback capability |
| **WhenIWork sync breaks** after changes | High | Low | Extensive integration testing, monitor sync logs post-launch |
| **User confusion** during transition | Medium | Medium | In-app guidance, release notes, training for power users |
| **Performance degradation** with large team lists | Medium | Low | Pagination, lazy loading, database indexes |
| **Lost clock PINs** during migration | High | Low | Backup all PINs before migration, verify after |
| **Invitation emails not delivered** | Medium | Low | Email delivery monitoring, resend option |

## Open Questions

- [x] Keep both invite and admin-create flows? **Decision: Yes, keep both**
- [x] Continue supporting WhenIWork sync? **Decision: Yes, sync to unified system**
- [x] Page name: Employees vs Team Members? **Decision: "Team Members"**
- [x] Priority: Function vs Full Redesign? **Decision: Full Redesign**
- [ ] Should deactivated members be permanently deletable? (Currently soft-delete only)
- [ ] Max team members per store before pagination performance issues?
- [ ] Should we show multi-store assignments on this page or link to a separate view?

---

## Supporting Research

### Competitive Analysis

| Competitor | Approach | Learnings |
|------------|----------|-----------|
| **Gusto** | Single employee list with inline actions, side panel for details | Clean UX, minimal modals |
| **Deputy** | Card-based team view with quick actions | Visual approach, good for small teams |
| **Homebase** | Tab-based: Active/Inactive/All, inline PIN setup | Simple status filtering |
| **Square Team** | Minimal UI, role-based access toggles | Focus on what matters |

**Key Patterns to Adopt:**
- Inline actions over modals where possible
- Side panel for detail view (not full-page navigation)
- Clear visual indicators for status (active, login access, PIN)
- Prominent sync button with last-sync timestamp

### User Research

**Sources:**
- Support ticket analysis (employee management category)
- Informal feedback from store managers during onboarding
- Observation of current page usage patterns

**Key Findings:**
1. "I don't understand the difference between an employee and a user" — Most common confusion
2. "Why do I have to do three clicks to set a PIN?" — Friction in common task
3. "Did the sync work?" — Unclear feedback after WhenIWork sync
4. "Who changed this?" — No audit visibility

### Market Data

- Average pawn shop has 5-15 employees per location
- Multi-location owners (2-5 stores) represent ~30% of customer base
- WhenIWork is the dominant scheduling tool in this vertical
- Employee turnover in retail averages 60% annually — fast onboarding matters

---

## Document History

| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | December 2025 | Claude | Initial PRD draft |
