← back to Professional Directory
agents/web-agent/app/doctors/[slug]/page.tsx
94 lines
// /doctors/[slug] — individual professional public profile.
const PD_API = process.env.PD_API_BASE || 'http://127.0.0.1:9874';
async function getPro(id: string) {
try {
const r = await fetch(`${PD_API}/professionals?limit=1&min_confidence=0`, { next: { revalidate: 300 } });
// /professionals is filter-shaped, not single-id. Single-fetch is /professionals/:npi.
// We'll route by NPI in the URL slug; if it's a numeric id the pd-api detail route
// doesn't currently support single-by-id — fall back to the search endpoint.
return null;
} catch { return null; }
}
async function getProByNpi(npi: string) {
try {
const r = await fetch(`${PD_API}/professionals/${npi}`, { next: { revalidate: 300 } });
return r.ok ? await r.json() : null;
} catch { return null; }
}
export default async function DoctorPage({ params }: { params: { slug: string } }) {
const slug = String(params.slug || '');
// Slug is the NPI (10 digits) or the pro id — we support both.
const isNpi = /^\d{10}$/.test(slug);
let pro: any = null;
if (isNpi) pro = await getProByNpi(slug);
if (!pro) {
return (
<section className="section">
<h2>Doctor profile coming soon</h2>
<p style={{ color: 'var(--ink-soft)' }}>
Public physician profiles ship in Phase 2. For now, search by name to find a practice;
NPI-based slugs will resolve once we wire the lookup.
</p>
<a href="/search" className="card" style={{ display: 'inline-block', padding: '12px 22px', background: 'var(--moss)', color: '#fff', borderRadius: 999, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', fontSize: 14 }}>
Back to search →
</a>
</section>
);
}
return (
<section className="section" style={{ paddingTop: 56 }}>
<div className="eyebrow">Professional</div>
<h2>{titleCase(pro.full_name)}{pro.title ? `, ${pro.title}` : ''}</h2>
<p style={{ color: 'var(--ink-soft)', fontSize: 17 }}>
{pro.primary_specialty || 'Specialty TBD'}
{pro.medical_school ? ` · ${pro.medical_school}` : ''}
{pro.graduation_year ? ` · Class of ${pro.graduation_year}` : ''}
</p>
<div className="card-grid" style={{ marginTop: 32 }}>
<div className="card">
<span className="badge">License</span>
<span className="name">{pro.license_status || 'Active'}</span>
<span className="meta">CA License #{pro.license_number || '—'} · NPI {pro.npi_number}</span>
</div>
<div className="card">
<span className="badge">Specialties</span>
<span className="name">{pro.specialties?.length || 1} on file</span>
<span className="meta">{(pro.specialties || []).slice(0, 4).join(' · ') || pro.primary_specialty || ''}</span>
</div>
<div className="card">
<span className="badge">Profile</span>
<span className="name">{pro.claim_status === 'verified' ? '✓ Verified' : 'Unclaimed'}</span>
<span className="meta">{pro.claim_status === 'verified' ? 'Doctor manages this page' : 'Auto-generated from public records'}</span>
</div>
</div>
<section style={{ marginTop: 56 }}>
<div className="eyebrow">Reviews</div>
<h2 style={{ marginTop: 14 }}>Patient ratings</h2>
<p style={{ color: 'var(--ink-soft)' }}>Phase 3 — reviews + ratings UI coming.</p>
</section>
<section style={{ marginTop: 56, borderTop: '1px solid var(--line)', paddingTop: 56 }}>
<h2>Is this you?</h2>
<p style={{ maxWidth: 580, color: 'var(--ink-soft)' }}>
Claim this profile to respond to reviews, message patients, and edit page content.
Verify with your NPI + practice domain email.
</p>
<a href={`/account/claim?npi=${pro.npi_number}`} className="card" style={{ display: 'inline-block', padding: '12px 22px', textAlign: 'center', background: 'var(--moss)', color: '#fff', borderRadius: 999, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', fontSize: 14 }}>
Claim this profile →
</a>
</section>
</section>
);
}
function titleCase(s: string) { return String(s || '').toLowerCase().replace(/\b\w/g, c => c.toUpperCase()); }