npm package: @kern-di/prospect-tracker
Description: React prospect engagement modal with scroll/idle tracking and notify events
Latest version: 0.1.13

# @kern-di/prospect-tracker

React component: scroll/idle engagement tracking, optional modal CTA, and `visit` / `cta_click` POSTs to your notify endpoint. The default URL is `https://admin.<apex>/api/notify` derived from `window.location.hostname` (e.g. on `www.kern-di.de` → `https://admin.kern-di.de/api/notify`). Override with `notifyUrl` if your admin host does not match that pattern.

**Styling is inline** (no Tailwind in consumer apps). Colors and type styles match **`@kern/ui` / `globals.css`** via **`@kern/shared/lib/kernUiTokens`**, which is **bundled** into the published package (no extra npm dependency).

## Install

```bash
pnpm add @kern-di/prospect-tracker
```

Peer dependencies: `react` ^18 and `react-dom` ^18 (portal + modal layer).

## Local demo (develop UI & behavior)

From the **monorepo root**:

```bash
pnpm prospect-tracker:demo
```

Opens **http://localhost:7080** — a five-section landing page plus footer, `ProspectTracker` wired to a **mock** `POST /api/notify` (logs payloads in the terminal). Imports the component from `src/` so edits hot-reload without running `tsup`. On each **full refresh**, the demo clears `kern_shown_*` / `kern_engaged_*` so the modal can trigger again.

## Usage

### Backend requirement (fleet / recommended)

For fleet/customer sites, the recommended setup is:

- **Frontend** posts to same-origin `notifyUrl="/api/notify"` (no CORS headaches)
- Your site implements **`POST /api/notify`** (Vercel `api/notify.ts`) which forwards to KERN’s central API (bearer-protected)
- Your site exposes the `recipientId` from a **backend env var** (not a Vite public env var) via a same-origin config route

For fleet/customer sites, implement a same-origin **`POST /api/notify`** route that forwards to KERN’s central API (bearer-protected).

Vercel one-liner:

```ts
import { createVercelHandler } from "@kern-di/prospect-tracker/server";

export default createVercelHandler({
	apiKey: process.env.KERN_API_KEY ?? "",
	// Optional; if omitted uses KERN_API_URL / VITE_KERN_API_URL / production default.
	apiUrl: process.env.KERN_API_URL,
});
```

#### Backend: provide `recipientId` via server env

Create `api/prospect-tracker-config.ts` (Vercel Node runtime) to expose the configured recipient id:

```ts
import type { VercelRequest, VercelResponse } from "@vercel/node";

export default function handler(req: VercelRequest, res: VercelResponse) {
	if (req.method !== "GET") {
		return res.status(405).json({ error: "Method not allowed" });
	}

	const recipientId = process.env.PROSPECT_RECIPIENT_ID?.trim() ?? "";
	if (!recipientId) {
		return res.status(503).json({ error: "PROSPECT_RECIPIENT_ID not configured" });
	}

	return res.status(200).json({ recipientId });
}
```

Set `PROSPECT_RECIPIENT_ID` on your Vercel project (server-side env var).

#### Frontend: mount once near app root

Fetch the recipient id and only render the tracker if it exists. Also set `notifyUrl="/api/notify"` so events hit your same-origin backend route.

```tsx
import { useEffect, useState } from "react";
import { ProspectTracker } from "@kern-di/prospect-tracker";

export function App() {
  const [recipientId, setRecipientId] = useState<string | null>(null);

  useEffect(() => {
    fetch("/api/prospect-tracker-config")
      .then((r) => (r.ok ? r.json() : null))
      .then((data) => setRecipientId(data?.recipientId ?? null))
      .catch(() => setRecipientId(null));
  }, []);

  return (
    <>
      {/* Your page */}
      {recipientId ? (
        <ProspectTracker
          recipientId={recipientId}
          notifyUrl="/api/notify"
          engagementSectionId="leistungen"
        />
      ) : null}
    </>
  );
}
```

### Props

| Prop | Description |
|------|-------------|
| `recipientId` | Required. Sent with notify events. |
| `notifyUrl` | Optional. Override POST URL if it is not `https://admin.<your-apex-domain>/api/notify`. |
| `engagementSectionId` | Optional. If the element exists, scrolling past it counts as engagement; otherwise depth/bottom heuristics apply. |
| `primaryColor` | Optional. Primary button background (hex/CSS color). |
| `headline`, `description`, `ctaLabel` | Optional. Override modal copy (defaults are warm, KERN-Di.–specific). |
| `thankYouTitle`, `thankYouMessage` | Optional. Copy after the CTA is clicked. |
| `showTeamPhoto` | Optional. Show the bundled Simon & Johannes photo in the modal (`true` by default). Set `false` for a minimal modal. |

## AI / coding-agent integration

Short copy-paste prompt: **[AGENT_PROMPT.md](./AGENT_PROMPT.md)** (also in `node_modules/@kern-di/prospect-tracker/` after install).

Fleet integration instructions (backend route that forwards to the central API): **[CODING_AGENT.md](./CODING_AGENT.md)** (also in `node_modules/@kern-di/prospect-tracker/` after install).

## Your workflow (keep this)

### 1. While you edit the package

From the **repo root**:

```bash
pnpm prospect-tracker:watch
```

Leave it running. It rebuilds `dist/` when `src/` changes. **Nothing is sent to npm.**

### 2. One-time: make npm let you publish

Do this once per machine (or when a publish returns `403`):

1. On [npmjs.com](https://www.npmjs.com/) → **Access Tokens** → **Generate New Token** → **Granular Access Token**.
2. Allow **read/write** for **`@kern-di/prospect-tracker`** (or the whole **`@kern-di`** scope).
3. Enable **Bypass two-factor authentication** (or the automation option npm shows). Without this, publish will keep failing with `403` if your account uses 2FA.
4. Save the token somewhere safe (password manager). **Do not commit it.**

Put it on this computer:

```bash
npm config set //registry.npmjs.org/:_authToken "paste_token_here"
```

(If you ever paste a token in chat or a screenshot, **revoke it on npm and make a new one**.)

You also need permission to publish under the **`@kern-di`** org on npm.

### 3. When you want a new version on npm

From the **repo root** (patch bump + build + type-check + publish):

```bash
pnpm prospect-tracker:release
```

Or from **`packages/prospect-tracker`**:

```bash
pnpm release
```

That bumps the patch version (e.g. `0.1.2` → `0.1.3`), runs **`scripts/publish.mjs`** (build → type-check → `pnpm publish`), then you **commit** `package.json` (and root `pnpm-lock.yaml` if it changed).

**Publish the current version only** (no version bump), e.g. after a failed publish:

```bash
pnpm prospect-tracker:publish
# or: pnpm --filter @kern-di/prospect-tracker publish:npm
```

Extra args are forwarded to `pnpm publish` (dry-run, OTP):

```bash
pnpm --filter @kern-di/prospect-tracker publish:npm -- --dry-run
pnpm --filter @kern-di/prospect-tracker publish:npm -- --otp=123456
```

### 4. If `release` failed *after* bumping the version

You’ll see `403` and `package.json` already has a **new** version that is **not** on npm yet. **Do not run `release` again** (you’d skip a version).

Fix the token (step 2), then publish **this** version only:

```bash
pnpm prospect-tracker:publish
```

If you don’t use a token and rely on OTP instead:

```bash
pnpm prospect-tracker:publish -- --otp=PASTE_6_DIGITS
```

Then commit as in step 3.