Add coach next-session prep views

This commit is contained in:
Flatlogic Bot 2026-06-09 14:19:59 +00:00
parent 0e65518e41
commit 22bb909af8
3 changed files with 178 additions and 7 deletions

View File

@ -48,6 +48,12 @@ router.get(
order: [["next_session_at", "ASC"]],
include: [{ model: db.packages, as: "package" }],
});
const upcomingPrepBriefs = await db.prep_briefs.findAll({
limit: 4,
order: [["next_session_at", "ASC"]],
where: { status: "ready" },
include: [{ model: db.clients, as: "client" }],
});
res.status(200).send({
counts: {
@ -59,6 +65,7 @@ router.get(
},
nextSessions,
activeClients,
upcomingPrepBriefs,
});
}),
);

View File

@ -23,6 +23,10 @@ type ActionItem = {
type PrepBrief = {
id: string;
previous_summary?: string;
open_commitments?: string;
suggested_questions?: string;
sensitive_topics?: string;
client_reflection?: string;
client_reflection_at?: string;
};
@ -77,6 +81,26 @@ function EmptyState({ label }: { label: string }) {
);
}
function PrepText({ value }: { value?: string }) {
if (!value) {
return <EmptyState label='No prep details saved yet.' />;
}
return (
<div className='space-y-2'>
{value
.split(/\n|;/)
.map((item) => item.trim())
.filter(Boolean)
.map((item) => (
<p key={item} className='text-sm leading-6 text-[#72798a]'>
{item}
</p>
))}
</div>
);
}
const Clients = () => {
const router = useRouter();
const [clients, setClients] = React.useState<Client[]>([]);
@ -108,6 +132,7 @@ const Clients = () => {
const latestReflection = selectedClient?.prep_briefs?.find((brief) => {
return Boolean(brief.client_reflection);
});
const latestPrepBrief = selectedClient?.prep_briefs?.[0];
function selectClient(clientId: string) {
setSelectedClientId(clientId);
@ -243,6 +268,74 @@ const Clients = () => {
</div>
</Panel>
<Panel>
<div className='border-b border-[#19192d]/10 p-5'>
<div className='flex items-center justify-between gap-4'>
<div>
<p className='text-xs font-semibold uppercase tracking-[0.22em] text-[#b17a1e]'>
Next-session prep
</p>
<h3 className='mt-2 text-lg font-semibold text-[#19192d]'>
Coach prep brief
</h3>
</div>
<span className='rounded-full bg-[#f3fbf8] px-3 py-1 text-xs font-semibold text-[#35b7a5]'>
Ready
</span>
</div>
</div>
{latestPrepBrief ? (
<div className='grid gap-4 p-5 lg:grid-cols-2'>
<div className='rounded-lg bg-[#fffdf9] p-4'>
<p className='text-xs font-semibold uppercase tracking-[0.18em] text-[#b17a1e]'>
Client reflection
</p>
<div className='mt-3'>
<PrepText value={latestPrepBrief.client_reflection} />
</div>
</div>
<div className='rounded-lg bg-[#fffdf9] p-4'>
<p className='text-xs font-semibold uppercase tracking-[0.18em] text-[#b17a1e]'>
Open commitments
</p>
<div className='mt-3'>
<PrepText value={latestPrepBrief.open_commitments} />
</div>
</div>
<div className='rounded-lg bg-[#fffdf9] p-4'>
<p className='text-xs font-semibold uppercase tracking-[0.18em] text-[#b17a1e]'>
Suggested questions
</p>
<div className='mt-3'>
<PrepText
value={latestPrepBrief.suggested_questions}
/>
</div>
</div>
<div className='rounded-lg bg-[#fffdf9] p-4'>
<p className='text-xs font-semibold uppercase tracking-[0.18em] text-[#b17a1e]'>
Watch points
</p>
<div className='mt-3'>
<PrepText value={latestPrepBrief.sensitive_topics} />
</div>
</div>
<div className='rounded-lg bg-[#fffdf9] p-4 lg:col-span-2'>
<p className='text-xs font-semibold uppercase tracking-[0.18em] text-[#b17a1e]'>
Previous summary
</p>
<div className='mt-3'>
<PrepText value={latestPrepBrief.previous_summary} />
</div>
</div>
</div>
) : (
<div className='p-5'>
<EmptyState label='No next-session prep brief yet.' />
</div>
)}
</Panel>
<div className='grid gap-4 lg:grid-cols-2'>
<Panel>
<div className='border-b border-[#19192d]/10 p-5'>

View File

@ -35,6 +35,17 @@ type Session = {
client?: Client;
};
type PrepBrief = {
id: string;
next_session_at?: string;
previous_summary?: string;
open_commitments?: string;
suggested_questions?: string;
sensitive_topics?: string;
client_reflection?: string;
client?: Client;
};
type Summary = {
counts: {
clients: number;
@ -45,6 +56,7 @@ type Summary = {
};
activeClients: Client[];
nextSessions: Session[];
upcomingPrepBriefs: PrepBrief[];
};
const emptySummary: Summary = {
@ -57,6 +69,7 @@ const emptySummary: Summary = {
},
activeClients: [],
nextSessions: [],
upcomingPrepBriefs: [],
};
function ShellCard({
@ -89,7 +102,7 @@ const Dashboard = () => {
loadSummary();
}, []);
const nextSession = summary.nextSessions[0];
const nextPrepBrief = summary.upcomingPrepBriefs[0];
const stats = [
{
href: '/clients',
@ -160,22 +173,24 @@ const Dashboard = () => {
<p className='text-xs font-semibold uppercase tracking-[0.22em] text-[#b17a1e]'>
Next session prep
</p>
{nextSession ? (
{nextPrepBrief ? (
<div>
<h2 className='mt-4 text-lg font-semibold text-[#19192d]'>
{nextSession.client?.name || 'Client session'}
{nextPrepBrief.client?.name || 'Client session'}
</h2>
<p className='mt-1 text-sm text-[#72798a]'>
{nextSession.title}
{nextPrepBrief.client?.role_title} ·{' '}
{nextPrepBrief.client?.company}
</p>
<p className='mt-5 leading-6 text-[#72798a]'>
{nextSession.ai_summary}
{nextPrepBrief.suggested_questions ||
nextPrepBrief.previous_summary}
</p>
<Link
href='/session-memory'
href={`/clients?clientId=${nextPrepBrief.client?.id}`}
className='mt-4 inline-flex items-center gap-2 rounded-full bg-[#35b7a5] px-3 py-1.5 text-sm font-semibold text-white'
>
Review memory
Open prep
<BaseIcon path={mdiArrowRight} size={18} />
</Link>
</div>
@ -292,6 +307,62 @@ const Dashboard = () => {
</div>
</ShellCard>
</div>
<ShellCard className='mt-4'>
<div className='border-b border-[#19192d]/10 p-4'>
<div className='flex items-center justify-between gap-4'>
<div>
<p className='text-xs font-semibold uppercase tracking-[0.22em] text-[#b17a1e]'>
Next-session prep
</p>
<h2 className='mt-2 text-lg font-semibold'>
Ready prep briefs
</h2>
</div>
<Link
className='text-sm font-semibold text-[#35b7a5]'
href='/clients'
>
Open clients
</Link>
</div>
</div>
<div className='grid gap-0 divide-y divide-[#19192d]/10 xl:grid-cols-2 xl:divide-x xl:divide-y-0'>
{summary.upcomingPrepBriefs.map((brief) => (
<Link
key={brief.id}
href={`/clients?clientId=${brief.client?.id}`}
className='block p-5 transition hover:bg-[#fffdf9]'
>
<div className='flex items-start justify-between gap-4'>
<div>
<p className='font-semibold text-[#19192d]'>
{brief.client?.name}
</p>
<p className='mt-1 text-sm text-[#72798a]'>
{brief.client?.role_title} · {brief.client?.company}
</p>
</div>
<span className='rounded-full bg-[#f3fbf8] px-3 py-1 text-xs font-semibold text-[#35b7a5]'>
Ready
</span>
</div>
<p className='mt-4 line-clamp-2 text-sm leading-6 text-[#72798a]'>
{brief.client_reflection ||
brief.suggested_questions ||
brief.previous_summary}
</p>
</Link>
))}
{summary.upcomingPrepBriefs.length === 0 && (
<p className='p-5 text-sm text-[#72798a]'>
{loading
? 'Loading prep briefs...'
: 'No prep briefs ready yet.'}
</p>
)}
</div>
</ShellCard>
</div>
</SectionMain>
</>