import { mdiAccountGroup, mdiArrowRight, mdiContentSaveOutline, mdiPlus, } from '@mdi/js'; import axios from 'axios'; import Head from 'next/head'; import Link from 'next/link'; import { useRouter } from 'next/router'; 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'; type ActionItem = { id: string; status: string; }; type Client = { id: string; name: string; email: string; status: string; company?: string; role_title?: string; tags?: string; next_session_at?: string; action_items?: ActionItem[]; }; type ClientForm = { name: string; email: string; status: string; company: string; role_title: string; tags: string; goals: string; notes: string; next_session_at: string; }; function emptyClientForm(): ClientForm { return { name: '', email: '', status: 'active', company: '', role_title: '', tags: '', goals: '', notes: '', next_session_at: '', }; } function displayDateTime(value?: string) { if (!value) { return 'Not scheduled'; } const date = new Date(value); if (Number.isNaN(date.getTime())) { return value; } return date.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', }); } function Panel({ children, className = '', }: { children: React.ReactNode; className?: string; }) { return (
{children}
); } function inputClass() { return 'w-full rounded-none border border-[#19192d]/10 bg-[#fffdf9] px-4 py-3 text-sm text-[#19192d] outline-none focus:border-[#35b7a5] focus:ring-2 focus:ring-[#35b7a5]/15'; } function Field({ label, children, }: { label: string; children: React.ReactNode; }) { return ( ); } const Clients = () => { const router = useRouter(); const [clients, setClients] = React.useState([]); const [clientForm, setClientForm] = React.useState(emptyClientForm()); const [isCreating, setIsCreating] = React.useState(false); const [isSaving, setIsSaving] = React.useState(false); const [notice, setNotice] = React.useState(''); React.useEffect(() => { loadClients(); }, []); async function loadClients() { const response = await axios.get('/coaching/clients'); setClients(response.data); } function updateClientForm(field: keyof ClientForm, value: string) { setClientForm((current) => { return { ...current, [field]: value, }; }); } async function createClient() { setIsSaving(true); setNotice(''); try { const response = await axios.post('/coaching/clients', { ...clientForm, next_session_at: clientForm.next_session_at || null, }); await loadClients(); setClientForm(emptyClientForm()); setIsCreating(false); await router.push(`/clients/${response.data.id}`); } finally { setIsSaving(false); } } function openCreateForm() { setIsCreating(true); setNotice(''); } return ( <> {getPageTitle('Clients')}
Client CRM

Clients

Scan the practice, add a client, and open a dedicated client file when you need the full context.

{notice && (
{notice}
)} {isCreating && (

New client

Create a client record

updateClientForm('name', event.target.value) } className={inputClass()} /> updateClientForm('email', event.target.value) } className={inputClass()} /> updateClientForm('company', event.target.value) } className={inputClass()} /> updateClientForm('role_title', event.target.value) } className={inputClass()} /> updateClientForm('next_session_at', event.target.value) } className={inputClass()} /> updateClientForm('tags', event.target.value) } className={inputClass()} placeholder='executive, founder, delegation' />
)}

Coaching relationships

{clients.length} clients in this workspace

{clients.map((client) => { const openActions = (client.action_items || []).filter( (item) => item.status !== 'done', ).length; return ( ); })}
Client Status Next session Commitments Tags

{client.name}

{[client.role_title, client.company] .filter(Boolean) .join(' · ') || client.email}

{client.status} {displayDateTime(client.next_session_at)} {openActions} {(client.tags || '').trim() || '—'} Open
); }; Clients.getLayout = function getLayout(page: ReactElement) { return {page}; }; export default Clients;