import { useState, useEffect, useCallback } from 'react' export type EmployeeRole = 'OWNER' | 'MANAGER' | 'AGENT' export type InvitationStatus = 'accepted' | 'pending' | 'revoked' export interface TeamMember { id: string firstName: string lastName: string email: string phone: string | null role: EmployeeRole isActive: boolean createdAt: string updatedAt: string lastActiveAt: string | null invitationStatus: InvitationStatus } export interface TeamStats { total: number active: number pending: number inactive: number } export interface InvitePayload { firstName: string lastName: string email: string role: 'MANAGER' | 'AGENT' } const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? 'https://api.rentflow.com/api/v1' async function apiFetch( path: string, options?: RequestInit ): Promise { const res = await fetch(`${API_BASE}${path}`, { headers: { 'Content-Type': 'application/json' }, credentials: 'include', ...options, }) const json = await res.json() if (!res.ok) { const err = new Error(json.message ?? 'Request failed') as any err.code = json.error err.statusCode = res.status throw err } return json.data as T } // ─── useTeam ───────────────────────────────────────────────── export function useTeam() { const [members, setMembers] = useState([]) const [stats, setStats] = useState({ total: 0, active: 0, pending: 0, inactive: 0 }) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const fetchMembers = useCallback(async () => { setLoading(true) setError(null) try { const [membersData, statsData] = await Promise.all([ apiFetch('/team'), apiFetch('/team/stats'), ]) setMembers(membersData) setStats(statsData) } catch (err: any) { setError(err.message) } finally { setLoading(false) } }, []) useEffect(() => { fetchMembers() }, [fetchMembers]) // ── Invite ───────────────────────────────────────────────── const invite = useCallback(async (payload: InvitePayload) => { const result = await apiFetch<{ employee: TeamMember; invitationId: string }>( '/team/invite', { method: 'POST', body: JSON.stringify(payload) } ) // Optimistically add the pending member setMembers((prev) => [...prev, result.employee]) setStats((prev) => ({ ...prev, total: prev.total + 1, pending: prev.pending + 1, })) return result }, []) // ── Update role ──────────────────────────────────────────── const updateRole = useCallback(async (memberId: string, role: 'MANAGER' | 'AGENT') => { const updated = await apiFetch(`/team/${memberId}/role`, { method: 'PATCH', body: JSON.stringify({ role }), }) setMembers((prev) => prev.map((m) => (m.id === memberId ? { ...m, role: updated.role } : m)) ) return updated }, []) // ── Deactivate ───────────────────────────────────────────── const deactivate = useCallback(async (memberId: string) => { const updated = await apiFetch(`/team/${memberId}/deactivate`, { method: 'POST', }) setMembers((prev) => prev.map((m) => (m.id === memberId ? { ...m, isActive: false } : m)) ) setStats((prev) => ({ ...prev, active: prev.active - 1, inactive: prev.inactive + 1 })) return updated }, []) // ── Reactivate ───────────────────────────────────────────── const reactivate = useCallback(async (memberId: string) => { const updated = await apiFetch(`/team/${memberId}/reactivate`, { method: 'POST', }) setMembers((prev) => prev.map((m) => (m.id === memberId ? { ...m, isActive: true } : m)) ) setStats((prev) => ({ ...prev, active: prev.active + 1, inactive: prev.inactive - 1 })) return updated }, []) // ── Remove ───────────────────────────────────────────────── const remove = useCallback(async (memberId: string) => { await apiFetch<{ success: boolean }>(`/team/${memberId}`, { method: 'DELETE', }) const target = members.find((m) => m.id === memberId) setMembers((prev) => prev.filter((m) => m.id !== memberId)) setStats((prev) => ({ ...prev, total: prev.total - 1, active: target?.isActive ? prev.active - 1 : prev.active, pending: target?.invitationStatus === 'pending' ? prev.pending - 1 : prev.pending, inactive: !target?.isActive && target?.invitationStatus === 'accepted' ? prev.inactive - 1 : prev.inactive, })) }, [members]) return { members, stats, loading, error, refetch: fetchMembers, invite, updateRole, deactivate, reactivate, remove, } }