# Build the "Connectors" pattern: `ConnectorList`

## Stack (edit this block to match your project)
- React 18+ with TypeScript: one function component, typed props, no `any`.
- Tailwind CSS, using your design system's tokens if you have them.
- Presentational only: data comes in through props, decisions go out through callbacks. No fetching or global state inside.
- If your codebase already has conventions (naming, shared components, tokens), follow them over this spec.

## What the pattern is for
Explicit integrations that let the agent reach real systems, with per-connector state and scope.

Use it when: Assistants whose value depends on the user's own data living in other tools.

## What to build
A list of the outside systems an assistant can reach, each with its own state, permission scope and last sync. It can also show, per answer, which connectors were read and which were skipped. It renders on a settings page, in a side panel, or under an answer.

### Props
- `connectors`: Array<{ id: string; name: string; icon?: ReactNode; status: "connected" | "syncing" | "failed" | "off"; scope: string[]; access: "read" | "readWrite"; lastSyncAt?: Date; lastGoodSyncAt?: Date; error?: string }>, e.g. scope ["tickets", "macros"].
- `usage?`: Array<{ connectorId: string; read: string | null; skippedReason?: string }>, what one answer actually read from each, e.g. "3 tickets".
- `variant`: "settings" | "answer", a full management list or a compact per-answer footer.
- `onConnect(id: string)`, `onReconnect(id: string)`, `onDisconnect(id: string)` and `onEditScope(id: string)`: the component never authenticates or changes access itself.
- `summary?`: string, e.g. "3 of 4 healthy".
- `now?`: Date, for relative times in tests.

### States it must render
- Connected: name, "Read-only" or "Read and write", the scope list and "Synced 4 min ago".
- Syncing: a progress marker with the previous sync time still shown.
- Failed: this card only shows the error, when it started and the last good sync, with Reconnect. Other connectors are unaffected.
- Off: "Not connected" with Connect; it is never shown as available.
- Answer footer: one line per connector saying what was read ("1 account, 2 contracts"), skipped and why, or off.

### Interactions
- Reconnect, Connect, Disconnect and Edit scope call their callbacks with the connector id.
- Disconnect asks for confirmation, stating which scopes will stop being available.
- In the answer variant, selecting a connector line expands the items that were read, if the parent provides them in `usage`.

### Edge cases
- A connector that has never synced: show "Not synced yet" instead of a blank time.
- A stale sync (older than the parent's threshold): show the time in amber, so "connected" never implies fresh.
- A failure that affects only part of the scope: show the failing scope item, not a global error.
- An answer that used no connectors at all: the footer says so explicitly.

## Must avoid
A connected-account list with no indication of what was actually read or when it last synced.

## Engineering constraint
Show last-sync and permission scope per connector; failures need per-connector, not global, errors.

## Accessibility
- Each connector is a list item whose accessible name includes its status, e.g. "Team chat, sync failed".
- Status is conveyed in text as well as colour or icon; a change to failed is announced politely with the connector name.

## House rules
- Real `<button>`/`<input>` elements, full keyboard use, visible focus; announce status changes with `aria-live="polite"`; respect `prefers-reduced-motion`.
- All user-facing text comes from props (or a `labels` prop) so it can be translated.
- One consistent accent marks anything the AI made or is doing; amber means "needs a person", red means risky, green means done. Never rely on colour alone.
- Fits a 360 px wide container without horizontal scrolling.

## Acceptance checks
- [ ] Every state above can be rendered by passing props, with no internal fake data.
- [ ] Every interaction and edge case above behaves as described.
- [ ] Nothing in "Must avoid" can happen with this component.

## Deliver
1. The component file with its exported prop types.
2. A short usage example that renders each state.
