import { Router, Request, Response, NextFunction } from 'express' import { z } from 'zod' import { listEmployees, inviteEmployee, updateEmployeeRole, deactivateEmployee, reactivateEmployee, removeEmployee, } from '../services/teamService' import { requireCompanyAuth } from '../middleware/requireCompanyAuth' import { requireTenant } from '../middleware/requireTenant' import { requireSubscription } from '../middleware/requireSubscription' import { requireRole } from '../middleware/requireRole' const router = Router() // All team routes require a valid company employee session + active subscription router.use(requireCompanyAuth, requireTenant, requireSubscription) // ─── Validation schemas ─────────────────────────────────────── const inviteSchema = z.object({ firstName: z.string().min(1).max(64), lastName: z.string().min(1).max(64), email: z.string().email(), role: z.enum(['MANAGER', 'AGENT']), // OWNER cannot be invited }) const updateRoleSchema = z.object({ role: z.enum(['MANAGER', 'AGENT']), }) // ─── GET /team ──────────────────────────────────────────────── // List all employees for this company. // All roles can view the team list. router.get('/', async (req: Request, res: Response, next: NextFunction) => { try { const members = await listEmployees(req.companyId) res.json({ data: members }) } catch (err) { next(err) } }) // ─── GET /team/stats ────────────────────────────────────────── // Quick counts for dashboard stat cards. router.get('/stats', async (req: Request, res: Response, next: NextFunction) => { try { const members = await listEmployees(req.companyId) const stats = { total: members.length, active: members.filter((m) => m.isActive && m.invitationStatus === 'accepted').length, pending: members.filter((m) => m.invitationStatus === 'pending').length, inactive: members.filter((m) => !m.isActive && m.invitationStatus === 'accepted').length, } res.json({ data: stats }) } catch (err) { next(err) } }) // ─── POST /team/invite ──────────────────────────────────────── // Invite a new employee by email. OWNER only. router.post( '/invite', requireRole('OWNER'), async (req: Request, res: Response, next: NextFunction) => { try { const body = inviteSchema.parse(req.body) const result = await inviteEmployee( req.companyId, req.employee.id, body ) res.status(201).json({ data: result }) } catch (err) { next(err) } } ) // ─── PATCH /team/:id/role ───────────────────────────────────── // Change an employee's role. OWNER only. router.patch( '/:id/role', requireRole('OWNER'), async (req: Request, res: Response, next: NextFunction) => { try { const body = updateRoleSchema.parse(req.body) const updated = await updateEmployeeRole( req.companyId, req.employee.id, req.employee.role, req.params.id, body ) res.json({ data: updated }) } catch (err) { next(err) } } ) // ─── POST /team/:id/deactivate ──────────────────────────────── // Suspend access without deleting the record. OWNER only. router.post( '/:id/deactivate', requireRole('OWNER'), async (req: Request, res: Response, next: NextFunction) => { try { const updated = await deactivateEmployee( req.companyId, req.employee.role, req.params.id ) res.json({ data: updated }) } catch (err) { next(err) } } ) // ─── POST /team/:id/reactivate ──────────────────────────────── // Restore a deactivated employee's access. OWNER only. router.post( '/:id/reactivate', requireRole('OWNER'), async (req: Request, res: Response, next: NextFunction) => { try { const updated = await reactivateEmployee( req.companyId, req.employee.role, req.params.id ) res.json({ data: updated }) } catch (err) { next(err) } } ) // ─── DELETE /team/:id ───────────────────────────────────────── // Permanently remove an employee + revoke their Clerk session/invite. OWNER only. router.delete( '/:id', requireRole('OWNER'), async (req: Request, res: Response, next: NextFunction) => { try { const result = await removeEmployee( req.companyId, req.employee.role, req.params.id ) res.json({ data: result }) } catch (err) { next(err) } } ) export default router