← back to Professional Directory
agents/web-agent/app/account/page.tsx
119 lines
// /account — login + role + subscription state, all proxied via /api/* to pd-api.
'use client';
import { useEffect, useState } from 'react';
type WhoAmI = {
user: null | {
id: number; email: string; role: string; tier: string;
display_name?: string; claimed_professional_id?: number; claimed_organization_id?: number;
synthetic?: boolean;
};
};
export default function AccountPage() {
const [me, setMe] = useState<WhoAmI | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/auth/whoami', { credentials: 'include' })
.then(r => r.ok ? r.json() : { user: null })
.then(setMe)
.finally(() => setLoading(false));
}, []);
if (loading) return <section className="section"><h2>Loading…</h2></section>;
if (!me?.user) {
return (
<section className="section">
<div className="eyebrow">Account</div>
<h2>Sign in to drvouch</h2>
<p style={{ maxWidth: 540, color: 'var(--ink-soft)' }}>
Patients: Google sign-in, free, leave reviews, get personalized matches.
Doctors / office managers: claim your practice in 60 seconds (NPI + practice email).
</p>
<a className="card" href="/api/auth/google?returnTo=/account"
style={{ display: 'inline-block', marginTop: 16, padding: '12px 22px', background: 'var(--moss)', color: '#fff', borderRadius: 999, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', fontSize: 14 }}>
Continue with Google →
</a>
<p style={{ marginTop: 22, color: 'var(--ink-soft)', fontSize: 13 }}>
By signing in, you agree drvouch is non-clinical: reviews and messages are about service / scheduling / billing — not medical advice.
</p>
</section>
);
}
const u = me.user;
return (
<section className="section">
<div className="eyebrow">Account</div>
<h2>Welcome, {u.display_name || u.email}</h2>
<p style={{ color: 'var(--ink-soft)' }}>
{u.synthetic ? 'Loopback synthetic-admin (Steve).' : `Signed in as ${u.email}.`}
</p>
<div className="card-grid" style={{ marginTop: 32 }}>
<div className="card">
<span className="badge">Role</span>
<span className="name">{u.role}</span>
<span className="meta">
{u.role === 'patient' && 'Browse, leave reviews, suppress comments on your own page.'}
{u.role === 'doctor' && 'Respond to reviews, edit page, DM patients (paid tier).'}
{u.role === 'admin' && 'You see everything. Approve claims at /admin/doctor-claims.'}
{u.role === 'guest' && 'Read-only.'}
</span>
</div>
<div className="card">
<span className="badge">Tier</span>
<span className="name">{u.tier === 'paid' ? '★ Paid' : 'Free'}</span>
<span className="meta">
{u.tier === 'paid'
? 'DMs + threads + responses unlocked.'
: 'Free reads. Upgrade to participate.'}
</span>
{u.tier !== 'paid' && (
<SubscribeButton role={u.role} />
)}
</div>
{(u.claimed_professional_id || u.claimed_organization_id) && (
<div className="card">
<span className="badge">Claimed</span>
<span className="name">
{u.claimed_professional_id ? `Professional #${u.claimed_professional_id}` :
u.claimed_organization_id ? `Organization #${u.claimed_organization_id}` : ''}
</span>
<span className="meta">Edit your page content from this dashboard.</span>
</div>
)}
</div>
<form action="/api/auth/logout" method="POST" style={{ marginTop: 32 }}>
<button type="submit" style={{ background: 'transparent', border: '1px solid var(--ink)', color: 'var(--ink)', padding: '10px 18px', borderRadius: 999, fontWeight: 600, fontSize: 13, letterSpacing: '0.04em', textTransform: 'uppercase', cursor: 'pointer' }}>
Sign out
</button>
</form>
</section>
);
}
function SubscribeButton({ role }: { role: string }) {
const plan = role === 'doctor' ? 'doctor_pro_monthly' : 'patient_plus_monthly';
const onClick = async () => {
const r = await fetch('/api/subscriptions/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ plan }),
});
const j = await r.json();
if (j.url) window.location.href = j.url;
else alert(j.error || 'Checkout failed');
};
return (
<button onClick={onClick}
style={{ marginTop: 12, background: 'var(--moss)', color: '#fff', border: 0, padding: '10px 18px', borderRadius: 999, fontWeight: 600, fontSize: 13, letterSpacing: '0.04em', textTransform: 'uppercase', cursor: 'pointer', alignSelf: 'flex-start' }}>
Upgrade →
</button>
);
}