Add coach next-session prep views
This commit is contained in:
parent
0e65518e41
commit
22bb909af8
@ -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,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
@ -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'>
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user