import { mdiBookOpenVariant, mdiCalendarClock, mdiCheck, mdiCheckCircleOutline, mdiChevronRight, mdiFlagVariantOutline, mdiMessageReplyTextOutline, mdiPencilOutline, } from '@mdi/js'; import axios from 'axios'; import Head from 'next/head'; import React from 'react'; import type { ReactElement } from 'react'; import BaseIcon from '../components/BaseIcon'; import SectionMain from '../components/SectionMain'; import { getPageTitle } from '../config'; import LayoutAuthenticated from '../layouts/Authenticated'; import { useAppSelector } from '../stores/hooks'; type PortalClient = { id: string; name: string; email?: string; goals?: string; next_session_at?: string; sessions?: Array<{ id: string; title: string; shared_client_notes?: string }>; action_items?: Array<{ id: string; title: string; status: string }>; prep_briefs?: Array<{ id: string; client_reflection?: string; client_reflection_at?: string; }>; resources?: Array<{ id: string; title: string; description?: string; url?: string; }>; }; function Panel({ children, className = '', }: { children: React.ReactNode; className?: string; }) { return (
{children}
); } function formatDate(value?: string) { if (!value) { return 'Not scheduled'; } return new Intl.DateTimeFormat('en', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', }).format(new Date(value)); } const ClientPortal = () => { const { currentUser } = useAppSelector((state) => state.auth); const [clients, setClients] = React.useState([]); const [clientId, setClientId] = React.useState(''); const [portalClient, setPortalClient] = React.useState( null, ); const [updatingItemId, setUpdatingItemId] = React.useState(''); const [reflection, setReflection] = React.useState(''); const [reflectionSaved, setReflectionSaved] = React.useState(false); const isClientUser = currentUser?.app_role?.name === 'Client'; React.useEffect(() => { async function loadClients() { if (isClientUser) { const response = await axios.get('/coaching/client-portal/me'); setPortalClient(response.data); setClientId(response.data.id); const latestReflection = response.data.prep_briefs?.[0]?.client_reflection || ''; setReflection(latestReflection); setReflectionSaved(Boolean(latestReflection)); return; } const response = await axios.get('/coaching/clients'); setClients(response.data); if (response.data.length > 0) { setClientId(response.data[0].id); } } loadClients(); }, [currentUser?.email, isClientUser]); React.useEffect(() => { async function loadPortal() { if (!clientId) { return; } const response = await axios.get(`/coaching/client-portal/${clientId}`); setPortalClient(response.data); const latestReflection = response.data.prep_briefs?.[0]?.client_reflection || ''; setReflection(latestReflection); setReflectionSaved(Boolean(latestReflection)); } if (!isClientUser) { loadPortal(); } }, [clientId, isClientUser]); async function toggleItem(item: { id: string; status: string }) { const nextStatus = item.status === 'done' ? 'in_progress' : 'done'; setUpdatingItemId(item.id); const response = await axios.patch( `/coaching/client-portal/action-items/${item.id}`, { status: nextStatus }, ); setPortalClient((current) => { if (!current) { return current; } return { ...current, action_items: (current.action_items || []).map((currentItem) => { if (currentItem.id === item.id) { return response.data; } return currentItem; }), }; }); setUpdatingItemId(''); } async function saveReflection() { const response = await axios.post('/coaching/client-portal/reflection', { clientId: portalClient?.id, reflection, }); setPortalClient((current) => { if (!current) { return current; } const existingBriefs = current.prep_briefs || []; const otherBriefs = existingBriefs.filter((brief) => { return brief.id !== response.data.id; }); return { ...current, prep_briefs: [response.data, ...otherBriefs], }; }); setReflectionSaved(true); } const openItems = (portalClient?.action_items || []).filter((item) => { return item.status !== 'done'; }); const finishedCount = (portalClient?.action_items || []).filter( (item) => item.status === 'done', ).length; const latestSession = portalClient?.sessions?.[0]; return ( <> {getPageTitle('Client Portal')}
{!isClientUser && ( )} {portalClient && (

Client workspace

Hi, {portalClient.name}

This is the shared space for your coaching work: notes your coach approved, commitments you are working on, and resources for the next session.

Next session

{formatDate(portalClient.next_session_at)}

Add your reflection below before the call.

Open commitments

{openItems.length}

Completed

{finishedCount}

Shared resources

{portalClient.resources?.length || 0}

Commitments

Mark what you completed before the next session.

{(portalClient.action_items || []).map((item) => { const isDone = item.status === 'done'; return ( ); })}

Pre-session reflection

Share what changed since the last call.