feat: disposition modal, persistent top bar, pagination, QA fixes

- DispositionModal: single modal for all call endings. Dismissable (agent can resume call).
  Agent clicks End → modal → select reason → hangup + dispose. Caller disconnects → same modal.
- One call screen: CallWidget stripped to ringing notification + auto-redirect to Call Desk.
- Persistent top bar in AppShell: agent status toggle + network indicator on all pages.
- Network indicator always visible (Connected/Unstable/No connection).
- Pagination: Untitled UI PaginationCardDefault on Call History + Appointments (20/page).
- Pinned table headers/footers: sticky column headers, scrollable body, pinned pagination.
  Applied to Call Desk worklist, Call History, Appointments, Call Recordings, Missed Calls.
- "Patient" → "Caller" column label in Call History.
- Offline → Ready toggle enabled.
- Profile status dot reflects Ozonetel state.
- NavAccountCard: popover placement top, View Profile + Account Settings restored.
- WIP pages for /profile and /account-settings.
- Enquiry form PHONE_INQUIRY → PHONE enum fix.
- Force Ready / View Profile / Account Settings removed then restored properly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-25 20:29:54 +05:30
parent daa2fbb0c2
commit e6b2208077
21 changed files with 645 additions and 816 deletions

View File

@@ -1,11 +1,17 @@
import { useEffect, type ReactNode } from 'react';
import { useLocation } from 'react-router';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faWifi, faWifiSlash } from '@fortawesome/pro-duotone-svg-icons';
import { Sidebar } from './sidebar';
import { SipProvider } from '@/providers/sip-provider';
import { useSip } from '@/providers/sip-provider';
import { CallWidget } from '@/components/call-desk/call-widget';
import { MaintOtpModal } from '@/components/modals/maint-otp-modal';
import { AgentStatusToggle } from '@/components/call-desk/agent-status-toggle';
import { useAuth } from '@/providers/auth-provider';
import { useMaintShortcuts } from '@/hooks/use-maint-shortcuts';
import { useNetworkStatus } from '@/hooks/use-network-status';
import { cx } from '@/utils/cx';
interface AppShellProps {
children: ReactNode;
@@ -15,6 +21,9 @@ export const AppShell = ({ children }: AppShellProps) => {
const { pathname } = useLocation();
const { isCCAgent } = useAuth();
const { isOpen, activeAction, close } = useMaintShortcuts();
const { connectionStatus, isRegistered } = useSip();
const networkQuality = useNetworkStatus();
const hasAgentConfig = !!localStorage.getItem('helix_agent_config');
// Heartbeat: keep agent session alive in Redis (CC agents only)
useEffect(() => {
@@ -39,7 +48,29 @@ export const AppShell = ({ children }: AppShellProps) => {
<SipProvider>
<div className="flex h-screen bg-primary">
<Sidebar activeUrl={pathname} />
<main className="flex flex-1 flex-col overflow-hidden">{children}</main>
<div className="flex flex-1 flex-col overflow-hidden">
{/* Persistent top bar — visible on all pages */}
{hasAgentConfig && (
<div className="flex shrink-0 items-center justify-end gap-2 border-b border-secondary px-4 py-2">
<div className={cx(
'flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium',
networkQuality === 'good'
? 'bg-success-primary text-success-primary'
: networkQuality === 'offline'
? 'bg-error-secondary text-error-primary'
: 'bg-warning-secondary text-warning-primary',
)}>
<FontAwesomeIcon
icon={networkQuality === 'offline' ? faWifiSlash : faWifi}
className="size-3"
/>
{networkQuality === 'good' ? 'Connected' : networkQuality === 'offline' ? 'No connection' : 'Unstable'}
</div>
<AgentStatusToggle isRegistered={isRegistered} connectionStatus={connectionStatus} />
</div>
)}
<main className="flex flex-1 flex-col overflow-hidden">{children}</main>
</div>
{isCCAgent && pathname !== '/' && pathname !== '/call-desk' && <CallWidget />}
</div>
<MaintOtpModal isOpen={isOpen} onOpenChange={(open) => !open && close()} action={activeAction} />