← back to Professional Directory

agents/web-agent/app/page.tsx

91 lines

// Homepage — search hero + ratio ribbon + grid of recently-vouched orgs.
// Hits pd-api on loopback for the live data.

const PD_API = process.env.PD_API_BASE || 'http://127.0.0.1:9874';

async function fetchJson(path: string) {
  try {
    const r = await fetch(`${PD_API}${path}`, { next: { revalidate: 300 }, headers: { Accept: 'application/json' } });
    if (!r.ok) return null;
    return await r.json();
  } catch { return null; }
}

async function getCounts() {
  return fetchJson('/health');
}

async function getRecentOrgs() {
  return fetchJson('/organizations?has_email=true&limit=12&sort=score');
}

export default async function HomePage() {
  const [health, orgs] = await Promise.all([getCounts(), getRecentOrgs()]);
  const c = health?.counts || {};
  const orgRows: any[] = orgs?.rows || [];

  return (
    <>
      <section className="hero">
        <div className="eyebrow">Patient-vouched LA healthcare</div>
        <h1>Find a doctor your community vouches&nbsp;for.</h1>
        <p className="lead">
          Real reviews from Angelenos. Real ratings on service, price, and quality.
          No algorithm games. {Number(c.organizations || 0).toLocaleString()} clinics, hospitals, and practices.
        </p>
        <form className="search-shell" action="/search" method="GET">
          <input type="text" name="q" placeholder="Search doctor, specialty, or city" autoFocus />
          <button type="submit">Search</button>
        </form>

        <div className="stat-ribbon">
          <div className="stat"><b>{Number(c.professionals || 0).toLocaleString()}</b>Professionals listed</div>
          <div className="stat"><b>{Number(c.organizations || 0).toLocaleString()}</b>Practices &amp; facilities</div>
          <div className="stat"><b>{Number(c.specialties || 0).toLocaleString()}</b>Specialties covered</div>
          <div className="stat"><b>LA</b>County, every ZIP</div>
        </div>
      </section>

      <section className="section">
        <div className="eyebrow">Recently active</div>
        <h2>Practices people are talking about</h2>
        {orgRows.length === 0 ? (
          <p style={{ color: 'var(--ink-soft)' }}>No data yet — pd-api may be still warming up.</p>
        ) : (
          <div className="card-grid">
            {orgRows.map((o) => (
              <a key={o.id} href={`/orgs/${o.id}`} className="card">
                <span className="badge">{titleCase(o.type)}</span>
                <span className="name">{titleCase(o.name)}</span>
                <span className="meta">
                  {[titleCase(o.city || ''), o.zip].filter(Boolean).join(' · ')}
                  {o.has_email ? ' · ✉ on file' : ''}
                </span>
                <span className="stars">★★★★☆</span>
              </a>
            ))}
          </div>
        )}
      </section>

      <section className="section" style={{ borderTop: '1px solid var(--line)' }}>
        <div className="eyebrow">For practitioners</div>
        <h2>Claim your practice. Respond to patients. Edit your page.</h2>
        <p style={{ maxWidth: 580, color: 'var(--ink-soft)' }}>
          drvouch automatically builds a draft profile for every licensed practice in LA.
          Doctors and office managers can claim theirs in 60 seconds (NPI + practice email)
          to respond to reviews, message patients directly, and customize the page content.
        </p>
        <div style={{ display: 'flex', gap: 12, marginTop: 18, flexWrap: 'wrap' }}>
          <a href="/account/claim" className="btn-primary" style={{ background: 'var(--moss)', color: '#fff', padding: '12px 22px', borderRadius: 999, fontWeight: 600, fontSize: 14, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Claim a practice</a>
          <a href="/community" style={{ padding: '12px 22px', borderBottom: '1px solid var(--ink)', fontWeight: 600, fontSize: 14, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Community →</a>
        </div>
      </section>
    </>
  );
}

function titleCase(s: string) {
  return String(s || '').toLowerCase().replace(/\b\w/g, c => c.toUpperCase());
}