# PRP-010 — Upgrade funnel & Pro teasers

- **Status:** Draft
- **Plan:** Lite
- **Depends on:** PRP-009 (lead form), and benefits from 002/004/005/007 teasers existing
- **Estimated effort:** ~1 day
- **Pro KB ref:** §5.5 + whole KB (feature catalog)

> **Audience:** junior dev. Earlier PRPs each added ad-hoc "disabled + PRO badge" buttons. This PRP
> **standardizes** them into one reusable component, builds a `/pro` showcase page, and defines CTA
> routing (open `PRO_UPGRADE_URL` vs. open the lead form from PRP-009).

---

## 1. Goal / Why
Lite exists to convert. A single, consistent Pro-teaser pattern (look + behavior) across the app makes
the upsell coherent and lets each module declare *what* is locked without re-styling it.

## 2. Scope
**In:** `<ProBadge>` + `<ProFeature>` components; CTA routing (EXTERNAL link / LEAD form); a `/pro`
showcase page driven by `/api/features`; refactor existing ad-hoc teasers to use the component.
**Out:** implementing any Pro feature backend.

## 3. Prerequisites
- PRP-009 merged (`LeadForm` component + `POST /api/leads`).
- `getFeatures()` returns `{ upgradeUrl, dailyCap, features: { key: { enabled, plan, label, teaser } } }`.

## 4. Step-by-step implementation

### Step 1 — A tiny global "open lead form" mechanism
So any teaser can open the lead modal. Simplest approach without extra libs: a React context.

Create **`client/src/upgrade.tsx`**:
```tsx
import { createContext, useContext, useState, ReactNode } from 'react';
import LeadForm from './components/LeadForm';

type Ctx = { openLead: (kind?: 'sales' | 'callback' | 'upgrade') => void };
const UpgradeCtx = createContext<Ctx>({ openLead: () => {} });
export const useUpgrade = () => useContext(UpgradeCtx);

export function UpgradeProvider({ children }: { children: ReactNode }) {
  const [open, setOpen] = useState(false);
  const [kind, setKind] = useState<'sales' | 'callback' | 'upgrade'>('sales');
  return (
    <UpgradeCtx.Provider value={{ openLead: (k = 'sales') => { setKind(k); setOpen(true); } }}>
      {children}
      {open && <LeadForm kind={kind} onClose={() => setOpen(false)} />}
    </UpgradeCtx.Provider>
  );
}
```
Wrap the app: in **`client/src/App.tsx`**, put `<UpgradeProvider>` inside `<AuthProvider>` around the
routes.

### Step 2 — `ProBadge` and `ProFeature` components
**`client/src/components/ProBadge.tsx`**:
```tsx
export default function ProBadge() {
  return <span className="lock">PRO</span>;
}
```

**`client/src/components/ProFeature.tsx`**:
```tsx
import { ReactNode, useEffect, useState } from 'react';
import { getFeatures, UPGRADE_URL_FALLBACK } from '../lib/features';
import { useUpgrade } from '../upgrade';
import ProBadge from './ProBadge';

type Props = {
  featureKey?: string;          // key in /features for the tooltip teaser
  label: string;                // button/control text
  cta?: 'EXTERNAL' | 'LEAD';    // default EXTERNAL
  block?: boolean;              // render as a full card instead of a button
};

// A locked Pro affordance: disabled-looking control that routes upgrade intent.
export default function ProFeature({ featureKey, label, cta = 'EXTERNAL', block }: Props) {
  const [upgradeUrl, setUpgradeUrl] = useState(UPGRADE_URL_FALLBACK);
  const [teaser, setTeaser] = useState<string | null>(null);
  const { openLead } = useUpgrade();

  useEffect(() => {
    getFeatures().then((f) => {
      setUpgradeUrl(f.upgradeUrl);
      if (featureKey && f.features[featureKey]) setTeaser(f.features[featureKey].teaser);
    }).catch(() => {});
  }, [featureKey]);

  const onClick = () => {
    if (cta === 'LEAD') return openLead('upgrade');
    window.open(upgradeUrl, '_blank', 'noopener');
  };

  if (block) {
    return (
      <div className="upgrade" onClick={onClick} role="button" style={{ cursor: 'pointer' }}>
        <strong>{label} <ProBadge /></strong>
        {teaser && <p className="muted">{teaser}</p>}
      </div>
    );
  }
  return (
    <button className="secondary" onClick={onClick} title={teaser || 'Pro feature'}>
      {label} <ProBadge />
    </button>
  );
}
```

### Step 3 — Refactor existing ad-hoc teasers to use `<ProFeature>`
Replace the inline `disabled` PRO buttons added in earlier PRPs with `<ProFeature .../>`:
- **Orders toolbar** (PRP-004): `bulk`, `import`, `drivers`, order type, returns.
- **Zones** (PRP-005): add/edit/delete/set-default + AI matching → use `block` cards where it reads as a
  showcase.
- **Settings → Team & Roles** (PRP-002): `team_users`.
- **Dashboard report tiles** (PRP-007): use `block` cards with `featureKey="reports"` etc.
Pick `cta="LEAD"` for high-intent placements (e.g. a "Talk to sales" button) and `cta="EXTERNAL"`
(default) for generic "learn more" teasers.

### Step 4 — `/pro` showcase page
Create **`client/src/pages/Pro.tsx`** that calls `getFeatures()` and renders every feature where
`plan === 'pro'` as a `<ProFeature block label={f.label} featureKey={key} />` grid, with a headline and
a primary **"Talk to sales"** (`cta="LEAD"`) and **"Visit shiprexnow.com"** (`cta="EXTERNAL"`) button.
Add the route in `App.tsx`: `<Route path="/pro" element={<Protected><Pro /></Protected>} />` and a
sidebar link "Explore Pro".

### Step 5 — Build & verify (see §6).

---

## 5. API endpoints & Postman documentation
**No new endpoints.** Uses existing `GET /api/features` (already in Postman → Health & Features) and
`POST /api/leads` (added in PRP-009). No Postman change required for this PRP.

---

## 6. Manual test / acceptance verification
- Every locked control across Orders/Zones/Settings/Dashboard now renders via `<ProFeature>`
  (consistent look). Search the client for leftover inline `disabled` PRO buttons:
  ```bash
  grep -rn "lock\">PRO" client/src   # should mostly be inside ProBadge/ProFeature now
  ```
- Clicking an EXTERNAL teaser opens `https://www.shiprexnow.com` in a new tab.
- Clicking a LEAD teaser opens the lead form; submitting → 202 (PRP-009).
- `/pro` lists all Pro features with teasers.

**Acceptance criteria**
- [ ] Single shared component used for all teasers (no ad-hoc locked UI left).
- [ ] EXTERNAL CTAs open shiprexnow.com; LEAD CTAs open + submit the lead form.
- [ ] `/pro` shows the full Pro catalog from `/features`.

---

## 7. Git commits (follow in order)
**Commit 1 — upgrade provider + ProFeature/ProBadge**
```bash
git add client/src/upgrade.tsx client/src/components/ProFeature.tsx client/src/components/ProBadge.tsx client/src/App.tsx
git commit -m "feat(web): PRP-010 ProFeature/ProBadge + upgrade lead provider

Adds a reusable locked-Pro affordance with EXTERNAL/LEAD CTA routing and a
context to open the lead form from anywhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

**Commit 2 — refactor existing teasers + /pro showcase**
```bash
git add client/src
git commit -m "feat(web): PRP-010 unify teasers + add /pro showcase page

Refactors Orders/Zones/Settings/Dashboard teasers to <ProFeature> and adds a
/pro page listing all Pro features from /api/features.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>"
```

**Commit 3 — PRP status**
```bash
git add PRPs/PRP-010-upgrade-funnel.md PRPs/README.md
git commit -m "docs(prp): mark PRP-010 done"
```

---

## 8. Done checklist
- [ ] Provider + components added; app wrapped.
- [ ] All teasers refactored; `/pro` page done.
- [ ] EXTERNAL/LEAD CTAs verified; build passes.
- [ ] Commits made; PRP marked Done.
