diff --git a/src/components/call-desk/call-widget.tsx b/src/components/call-desk/call-widget.tsx index 592bab9..efab15f 100644 --- a/src/components/call-desk/call-widget.tsx +++ b/src/components/call-desk/call-widget.tsx @@ -14,6 +14,7 @@ import { import { Button } from '@/components/base/buttons/button'; import { TextArea } from '@/components/base/textarea/textarea'; import { useSip } from '@/providers/sip-provider'; +import { useAuth } from '@/providers/auth-provider'; import { cx } from '@/utils/cx'; import type { CallDisposition } from '@/types/entities'; @@ -97,6 +98,7 @@ export const CallWidget = () => { toggleMute, toggleHold, } = useSip(); + const { user } = useAuth(); const [disposition, setDisposition] = useState(null); const [notes, setNotes] = useState(''); @@ -180,7 +182,7 @@ export const CallWidget = () => { data: { callDirection: 'INBOUND', callStatus: 'COMPLETED', - agentName: 'Rekha S.', + agentName: user.name, startedAt: callStartTimeRef.current, endedAt: new Date().toISOString(), durationSeconds: callDuration, @@ -228,7 +230,7 @@ export const CallWidget = () => { activityType: 'CALL_RECEIVED', summary: `Inbound call — ${disposition.replace(/_/g, ' ')}`, occurredAt: new Date().toISOString(), - performedBy: 'Rekha S.', + performedBy: user.name, channel: 'PHONE', durationSeconds: callDuration, leadId: matchedLead.id, diff --git a/src/lib/api-client.ts b/src/lib/api-client.ts index 9329f97..2aa43ae 100644 --- a/src/lib/api-client.ts +++ b/src/lib/api-client.ts @@ -21,7 +21,19 @@ const clearTokens = () => { }; export const apiClient = { - async login(email: string, password: string): Promise<{ accessToken: string; refreshToken: string }> { + async login(email: string, password: string): Promise<{ + accessToken: string; + refreshToken: string; + user?: { + id?: string; + email?: string; + firstName?: string; + lastName?: string; + avatarUrl?: string; + role?: string; + platformRoles?: string[]; + }; + }> { const response = await fetch(`${API_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -33,9 +45,9 @@ export const apiClient = { throw new Error(data.message ?? 'Login failed'); } - const tokens = await response.json(); - storeTokens(tokens.accessToken, tokens.refreshToken); - return tokens; + const data = await response.json(); + storeTokens(data.accessToken, data.refreshToken); + return data; }, async graphql(query: string, variables?: Record): Promise { diff --git a/src/pages/login.tsx b/src/pages/login.tsx index 0f06d07..f390882 100644 --- a/src/pages/login.tsx +++ b/src/pages/login.tsx @@ -20,24 +20,16 @@ const features = [ }, ]; -type RoleTab = 'executive' | 'cc-agent' | 'admin'; export const LoginPage = () => { - const { login, setRole } = useAuth(); + const { loginWithUser } = useAuth(); const navigate = useNavigate(); - const [activeTab, setActiveTab] = useState('executive'); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); - const handleTabChange = (tab: RoleTab) => { - setActiveTab(tab); - setRole(tab); - setError(null); - }; - const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); setError(null); @@ -50,8 +42,25 @@ export const LoginPage = () => { setIsLoading(true); try { const { apiClient } = await import('@/lib/api-client'); - await apiClient.login(email, password); - login(); + const response = await apiClient.login(email, password); + + // Build user from sidecar response + const u = response.user; + const firstName = u?.firstName ?? ''; + const lastName = u?.lastName ?? ''; + const name = `${firstName} ${lastName}`.trim() || email; + const initials = `${firstName[0] ?? ''}${lastName[0] ?? ''}`.toUpperCase() || email[0].toUpperCase(); + + loginWithUser({ + id: u?.id, + name, + initials, + role: (u?.role ?? 'executive') as 'executive' | 'admin' | 'cc-agent', + email: u?.email ?? email, + avatarUrl: u?.avatarUrl, + platformRoles: u?.platformRoles, + }); + navigate('/'); } catch (err: any) { setError(err.message); @@ -60,7 +69,6 @@ export const LoginPage = () => { }; const handleGoogleSignIn = () => { - // TODO: implement Google OAuth via sidecar setError('Google sign-in not yet configured'); }; @@ -138,30 +146,7 @@ export const LoginPage = () => {

Sign in to Helix Engage

Global Hospital

- {/* Role selector tabs */} -
- {([ - { key: 'executive' as const, label: 'Marketing Executive' }, - { key: 'cc-agent' as const, label: 'Call Center' }, - { key: 'admin' as const, label: 'Admin' }, - ]).map((tab) => ( - - ))} -
+ {/* Role is determined by platform — no selector needed */} {/* Google sign-in */}
diff --git a/src/providers/auth-provider.tsx b/src/providers/auth-provider.tsx index 66ce9c0..c878af1 100644 --- a/src/providers/auth-provider.tsx +++ b/src/providers/auth-provider.tsx @@ -1,55 +1,46 @@ import type { ReactNode } from 'react'; -import { createContext, useContext, useState } from 'react'; +import { createContext, useCallback, useContext, useState } from 'react'; export type Role = 'executive' | 'admin' | 'cc-agent'; type User = { + id?: string; name: string; initials: string; role: Role; email: string; + avatarUrl?: string; + platformRoles?: string[]; }; type AuthContextType = { user: User; - setRole: (role: Role) => void; isAdmin: boolean; isCCAgent: boolean; isAuthenticated: boolean; + loginWithUser: (userData: User) => void; login: () => void; logout: () => void; + setRole: (role: Role) => void; }; -const USERS: Record = { - admin: { - name: 'Admin User', - initials: 'AU', - role: 'admin', - email: 'admin@globalhospital.com', - }, - executive: { - name: 'Sanjay M.', - initials: 'SM', - role: 'executive', - email: 'sanjay@globalhospital.com', - }, - 'cc-agent': { - name: 'Rekha S.', - initials: 'RS', - role: 'cc-agent' as const, - email: 'rekha@globalhospital.com', - }, +const DEFAULT_USER: User = { + name: '', + initials: '', + role: 'executive', + email: '', }; +const getInitials = (firstName: string, lastName: string): string => + `${firstName[0] ?? ''}${lastName[0] ?? ''}`.toUpperCase(); + const AuthContext = createContext(undefined); export const useAuth = (): AuthContextType => { const context = useContext(AuthContext); - if (context === undefined) { throw new Error('useAuth must be used within an AuthProvider'); } - return context; }; @@ -58,19 +49,39 @@ interface AuthProviderProps { } export const AuthProvider = ({ children }: AuthProviderProps) => { - const [role, setRole] = useState('executive'); + const [user, setUser] = useState(DEFAULT_USER); const [isAuthenticated, setIsAuthenticated] = useState(false); - const user = USERS[role]; - const isAdmin = role === 'admin'; - const isCCAgent = role === 'cc-agent'; + const isAdmin = user.role === 'admin'; + const isCCAgent = user.role === 'cc-agent'; - const login = () => setIsAuthenticated(true); - const logout = () => setIsAuthenticated(false); + // Real login — receives user profile from sidecar auth response + const loginWithUser = useCallback((userData: User) => { + setUser(userData); + setIsAuthenticated(true); + }, []); + + // Simple login (for backward compat) + const login = useCallback(() => { + setIsAuthenticated(true); + }, []); + + const logout = useCallback(() => { + setUser(DEFAULT_USER); + setIsAuthenticated(false); + localStorage.removeItem('helix_access_token'); + localStorage.removeItem('helix_refresh_token'); + }, []); + + const setRole = useCallback((role: Role) => { + setUser(prev => ({ ...prev, role })); + }, []); return ( - + {children} ); }; + +export { getInitials };