import { useState, useEffect } from 'react' import { useParams, useNavigate, Link } from 'react-router-dom' import { Globe, Users, ShieldAlert, FileX, Clock } from 'lucide-react' import { isAxiosError } from 'axios' import { sessionsApi } from '@/api/sessions' import { Spinner } from '@/components/common/Spinner' import { BrandLogo } from '@/components/common/BrandLogo' import { PageMeta } from '@/components/common/PageMeta' import { SessionTimeline } from '@/components/session/SessionTimeline' import { SharedSessionTreePreview } from '@/components/session/SharedSessionTreePreview' import type { SharedSessionView } from '@/types' function formatDate(dateString: string) { return new Date(dateString).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric', }) } function formatDuration(startedAt: string, completedAt: string): string { const start = new Date(startedAt).getTime() const end = new Date(completedAt).getTime() const totalSeconds = Math.floor((end - start) / 1000) if (totalSeconds < 0) return '0s' if (totalSeconds < 60) return `${totalSeconds}s` const hours = Math.floor(totalSeconds / 3600) const minutes = Math.floor((totalSeconds % 3600) / 60) const seconds = totalSeconds % 60 if (hours > 0) { return seconds > 0 ? `${hours}h ${minutes}m ${seconds}s` : `${hours}h ${minutes}m` } return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m` } type ErrorState = { type: 'access_denied' | 'not_found' | 'expired' | 'generic' message: string } function ErrorCard({ error }: { error: ErrorState }) { const iconMap = { access_denied: ShieldAlert, not_found: FileX, expired: Clock, generic: FileX, } const titleMap = { access_denied: 'Access Denied', not_found: 'Not Found', expired: 'Link Expired', generic: 'Error', } const Icon = iconMap[error.type] return (

{titleMap[error.type]}

{error.message}

Go to ResolutionFlow
) } export function SharedSessionPage() { const { shareToken } = useParams<{ shareToken: string }>() const navigate = useNavigate() const [data, setData] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { if (!shareToken) return let cancelled = false async function fetchSharedSession() { try { const result = await sessionsApi.getSharedSession(shareToken!) if (!cancelled) { setData(result) setLoading(false) } } catch (err) { if (cancelled) return if (isAxiosError(err)) { const status = err.response?.status if (status === 401) { navigate('/login', { state: { from: { pathname: `/share/${shareToken}` } }, replace: true, }) return } if (status === 403) { setError({ type: 'access_denied', message: 'This session is private to the account. You need to be a member of the account to view it.', }) } else if (status === 404) { setError({ type: 'not_found', message: 'This share link was not found or has been revoked.', }) } else if (status === 410) { setError({ type: 'expired', message: 'This share link has expired.', }) } else { setError({ type: 'generic', message: 'Failed to load shared session. Please try again.', }) } } else { setError({ type: 'generic', message: 'Failed to load shared session. Please try again.', }) } setLoading(false) } } fetchSharedSession() return () => { cancelled = true } }, [shareToken, navigate]) if (loading) { return (

Loading shared session...

) } if (error) { return } if (!data) { return null } return (
{/* Minimal header */}
ResolutionFlow Sign In
{/* Content */}
{/* Metadata section */}
{data.share_name && (

{data.share_name}

)}

Tree: {data.tree_name}

{(data.ticket_number || data.client_name) && (

{data.ticket_number && ( Ticket: #{data.ticket_number} )} {data.ticket_number && data.client_name && ( · )} {data.client_name && ( Client: {data.client_name} )}

)}
Started: {formatDate(data.started_at)} {data.completed_at && ( <> Completed: {formatDate(data.completed_at)} Duration: {formatDuration(data.started_at, data.completed_at)} )} {data.visibility === 'public' ? ( <> Public ) : ( <> Account )}
{/* Two-column layout */}
{/* Decision Timeline (2 cols) */}
{/* Tree Preview (1 col) */}
{/* Footer */}
) } export default SharedSessionPage