diff --git a/frontend/src/pages/client-portal.tsx b/frontend/src/pages/client-portal.tsx index 9097bfa..cff52e6 100644 --- a/frontend/src/pages/client-portal.tsx +++ b/frontend/src/pages/client-portal.tsx @@ -1,9 +1,12 @@ import { - mdiAccountCircle, mdiBookOpenVariant, + mdiCalendarClock, + mdiCheck, mdiCheckCircleOutline, mdiChevronRight, + mdiFlagVariantOutline, mdiMessageReplyTextOutline, + mdiPencilOutline, } from '@mdi/js'; import axios from 'axios'; import Head from 'next/head'; @@ -18,7 +21,9 @@ 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 }>; resources?: Array<{ @@ -45,15 +50,31 @@ function Panel({ ); } +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< - Array<{ id: string; name: string; email?: string }> - >([]); + const [clients, setClients] = React.useState([]); const [clientId, setClientId] = React.useState(''); const [portalClient, setPortalClient] = React.useState( null, ); + const [completedItems, setCompletedItems] = React.useState>( + new Set(), + ); + const [reflection, setReflection] = React.useState(''); + const [reflectionSaved, setReflectionSaved] = React.useState(false); const isClientUser = currentUser?.app_role?.name === 'Client'; @@ -87,11 +108,35 @@ const ClientPortal = () => { const response = await axios.get(`/coaching/client-portal/${clientId}`); setPortalClient(response.data); + setCompletedItems(new Set()); + setReflection(''); + setReflectionSaved(false); } loadPortal(); }, [clientId]); + function toggleItem(itemId: string) { + setCompletedItems((current) => { + const next = new Set(current); + if (next.has(itemId)) { + next.delete(itemId); + } else { + next.add(itemId); + } + + return next; + }); + } + + const openItems = (portalClient?.action_items || []).filter((item) => { + return item.status !== 'done' && !completedItems.has(item.id); + }); + const finishedCount = + (portalClient?.action_items || []).filter((item) => item.status === 'done') + .length + completedItems.size; + const latestSession = portalClient?.sessions?.[0]; + return ( <> @@ -99,176 +144,272 @@ const ClientPortal = () => {
-
-
-
- - - Client portal - -
-

- {isClientUser ? 'Your client portal' : 'Client portal preview'} -

-

- {isClientUser - ? 'Review shared notes, commitments, resources, and reflections for your coaching work.' - : 'Preview the client-facing workspace with shared notes, commitments, resources, and a pre-session reflection prompt.'} -

-
- {!isClientUser && ( - - - -

- Coaches can preview what each client sees before sharing - notes, commitments, or resources. -

-
- )} -
+ {!isClientUser && ( + + + + + )} {portalClient && ( -
- -
-

- Your coaching workspace -

-

- {portalClient.name} -

-

- {portalClient.goals} -

-
+
+
+
+
+

+ 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. +

+
-
-
- - - -
-

- Shared session notes -

-

- Only coach-approved notes appear here. -

+
+
+ + Next session
-
- -
- {(portalClient.sessions || []).map((session) => ( -
-

- {session.title} -

-

- {session.shared_client_notes} -

-
- ))} +

+ {formatDate(portalClient.next_session_at)} +

+

+ Add your reflection below before the call. +

- +
-
- -
-

- Commitments -

-
-
- {(portalClient.action_items || []).map((item) => ( -
- +
+ +

+ Open commitments +

+

+ {openItems.length} +

+
+ +

+ Completed +

+

+ {finishedCount} +

+
+ +

+ Shared resources +

+

+ {portalClient.resources?.length || 0} +

+
+
+ +
+
+ +
+
+
-

- {item.title} -

-

- {item.status.replace('_', ' ')} +

+ Commitments +

+

+ Mark what you completed before the next session.

- ))} -
-
+
+
+ {(portalClient.action_items || []).map((item) => { + const isDone = + item.status === 'done' || completedItems.has(item.id); - -
-

- Resources -

-
- +
+ + +
+ + + +
+

+ Pre-session reflection +

+

+ Share what changed since the last call. +

+
+
+