# @agg-build/ui

Pre-built React component library for the [AGG](https://docs.agg.market/) prediction market
aggregator. Tailwind-based styles, themeable CSS tokens, and components grouped into primitives,
event surfaces, trading flows, full pages, and modals.

See every component live in the [AGG Storybook](https://storybook.agg.market/).

## What it is

`@agg-build/ui` ships the UI you'd have to build yourself: event cards, market detail pages, orderbook
and chart primitives, order entry forms, deposit/withdraw/onboarding modals, and opinionated page
layouts. Components are built on top of [`@agg-build/hooks`](https://www.npmjs.com/package/@agg-build/hooks)
and expect an `<AggProvider>` above them in the tree. Styling is Tailwind v4 + CSS custom
properties so you can theme everything from your own stylesheet.

This package intentionally contains **no wallet code and no auth adapters** — the modular
connect/sign-in UI lives in [`@agg-build/auth`](https://www.npmjs.com/package/@agg-build/auth). That keeps
`@agg-build/ui` free of `wagmi`, `@solana/*`, and related peers.

## When to use this package

- You want production-ready AGG trading UI without wiring every view from scratch.
- You plan to render a full AGG home/event/market/profile surface inside your app.
- You want themeable primitives (Button, Card, Modal, Chart, Tooltip, etc.) consistent with the
  rest of the AGG surfaces.

If you only need the underlying logic, install [`@agg-build/sdk`](https://www.npmjs.com/package/@agg-build/sdk)
and [`@agg-build/hooks`](https://www.npmjs.com/package/@agg-build/hooks) and build your own UI.

## Install

```bash
npm install @agg-build/sdk @agg-build/hooks @agg-build/ui liveline
```

`liveline` is the charting engine used by `Chart` / `LineChart` and is declared as a peer
dependency.

## Quick start

```tsx
import "@agg-build/ui/styles.css";
import { AggProvider } from "@agg-build/hooks";
import { createAggClient } from "@agg-build/sdk";
import { HomePage } from "@agg-build/ui/pages";

const client = createAggClient({
  baseUrl: "https://api.agg.market",
  appId: "your-app-id",
  wsUrl: "wss://ws.agg.market",
});

export default function App() {
  return (
    <AggProvider
      client={client}
      config={{
        general: { locale: "en-US", theme: "dark" },
        features: { enableAnimations: true, enableLiveUpdates: true },
      }}
    >
      <HomePage withHeader />
    </AggProvider>
  );
}
```

## Styling

Import the default theme once at your app entrypoint:

```tsx
import "@agg-build/ui/styles.css";
```

Override theme tokens with CSS variables on the `.agg-root` container (automatically applied by
`AggProvider`):

```css
.agg-root {
  --agg-color-primary: #ed8200;
  --agg-color-foreground: #1a1a1a;
  --agg-radius-md: 8px;
  --agg-font-family-sans: "Inter", sans-serif;
}
```

If you already use Tailwind v4 in your app and want the AGG preset to merge with your config,
import `@agg-build/ui/tailwind.css` instead of (or alongside) `styles.css`.

## Tree-shaking

Import from the grouped subpaths to keep your bundle narrow:

```tsx
// Preferred
import { Button, Card } from "@agg-build/ui/primitives";
import { HomePage, EventMarketPage } from "@agg-build/ui/pages";
import { DepositModal, OnboardingModal } from "@agg-build/ui/modals";
import { PlaceOrder } from "@agg-build/ui/trading";

// Wider than necessary
import { Button } from "@agg-build/ui";
```

## Using a full page

The `HomePage` and `EventMarketPage` components take partner-owned navigation callbacks. In the
demo app we wire them to Next.js `router.push`:

```tsx
"use client";
import { HomePage } from "@agg-build/ui/pages";
import { useRouter } from "next/navigation";

export function Home() {
  const router = useRouter();
  const getMarketHref = (event, market, outcome) =>
    `/events/${event.id}?marketId=${market.id}&outcomeId=${outcome.id}`;
  const getEventHref = (event) => `/events/${event.id}`;

  return (
    <HomePage
      withHeader={false}
      onEventClick={(event) => router.push(getEventHref(event))}
      onMarketClick={(event, market, outcome) => router.push(getMarketHref(event, market, outcome))}
      getEventHref={getEventHref}
      getMarketHref={getMarketHref}
      stickyOrderPanel={{ enabled: true, top: 80 }}
    />
  );
}
```

## Components

### Primitives — `@agg-build/ui/primitives`

| Component            | Description                  |
| -------------------- | ---------------------------- |
| `AggErrorBoundary`   | Error boundary wrapper       |
| `AggLogo`            | AGG wordmark                 |
| `AutocompleteSelect` | Combobox with async results  |
| `Badge`              | Status badge                 |
| `Button`             | Base button primitive        |
| `Card`               | Content card                 |
| `Chart`              | Price chart (liveline-based) |
| `CurrencyInput`      | Locale-aware money input     |
| `Footer`             | Site footer                  |
| `Header`             | Site header with search      |
| `Icon`               | AGG icon set                 |
| `InlineAlert`        | Error/warning inline alert   |
| `LoadingIcon`        | Spinner                      |
| `Modal`              | Dialog/modal                 |
| `NumberValue`        | Formatted, animated numbers  |
| `RemoteImage`        | Image with loading/error     |
| `Search`             | Search input                 |
| `Select`             | Dropdown select              |
| `Skeleton`           | Loading placeholder          |
| `StateMessage`       | Empty / error state display  |
| `SwitchButton`       | Binary toggle switch         |
| `Tabs`               | Tab navigation               |
| `Tooltip`            | Hover / focus tooltip        |
| `Typography`         | Text scale primitives        |
| `VenueLogo`          | Venue icon / logo            |

### Events — `@agg-build/ui/events`

| Component       | Description                           |
| --------------- | ------------------------------------- |
| `EventList`     | Grid/list of prediction market events |
| `EventListItem` | Single event row / card               |
| `ItemDetails`   | Per-market detail block               |
| `MarketDetails` | Full market detail view               |
| `Orderbook`     | Orderbook depth view                  |

### Trading — `@agg-build/ui/trading`

| Component           | Description                            |
| ------------------- | -------------------------------------- |
| `PlaceOrder`        | Order entry form (managed execution)   |
| `Settlement`        | Settlement status card                 |
| `SettlementDetails` | Expanded settlement detail             |
| `TradingContext`    | Event/market/outcome selection context |

### Pages — `@agg-build/ui/pages`

| Component         | Description                            |
| ----------------- | -------------------------------------- |
| `HomePage`        | Full home page with tabs, search, grid |
| `EventMarketPage` | Full event/market detail page          |
| `UserProfilePage` | User profile page with tabs            |

### Modals — `@agg-build/ui/modals`

| Component         | Description                             |
| ----------------- | --------------------------------------- |
| `DepositModal`    | Deposit flow (managed wallets + chains) |
| `WithdrawModal`   | Withdraw flow                           |
| `OnboardingModal` | User onboarding flow                    |
| `ProfileModal`    | Profile edit + linked accounts          |
| `GeoBlockModal`   | Geo-restriction modal                   |
| `GeoBlockBanner`  | Persistent geo-block banner             |

### `classNames` overrides

Most components accept a `classNames` compound prop so partners can target sub-elements:

```tsx
<Modal.Header
  title="Place order"
  classNames={{
    root: "custom-header",
    title: "custom-title",
    close: "custom-close-btn",
  }}
/>
```

## Key component props

### `HomePage`

| Prop                  | Type                                 | Description                                         |
| --------------------- | ------------------------------------ | --------------------------------------------------- |
| `tabs`                | `HomePageTab[]`                      | Custom tab definitions                              |
| `eventSectionItems`   | `HomePageEventSectionItem[]`         | Custom event section configuration                  |
| `categoriesLimit`     | `number`                             | Max number of category tabs to show                 |
| `allCategoryTabLabel` | `string`                             | Label for the "All" category tab                    |
| `actions`             | `ReactNode`                          | Slot for header actions (e.g., `<ConnectButton />`) |
| `withHeader`          | `boolean` (default `true`)           | Render the built-in `Header` with search + logo     |
| `onEventClick`        | `(event) => void`                    | Called when an event card is clicked                |
| `onMarketClick`       | `(event, market, outcome) => void`   | Called when an outcome price is clicked             |
| `getEventHref`        | `(event) => string`                  | Provide `href` for SSR-friendly links               |
| `getMarketHref`       | `(event, market, outcome) => string` | Provide `href` for market links                     |
| `stickyOrderPanel`    | `{ enabled: boolean; top?: number }` | Stick the order panel when scrolling                |
| `classNames`          | `HomePageClassNames`                 | Style overrides                                     |

### `EventMarketPage`

Renders the full event page with trading context. Requires `<AggProvider>` and uses
`EventTradingContext` internally. Access selection state with `useEventTradingContext()` from
`@agg-build/hooks`.

### `PlaceOrder`

Order entry form. Renders inside `EventMarketPage` or standalone — requires an active
`EventTradingContext` with a selected market and outcome. Integrates with the managed execution
hooks from `@agg-build/hooks`.

## Entrypoints

| Entry                        | Purpose                                                    |
| ---------------------------- | ---------------------------------------------------------- |
| `@agg-build/ui`              | Root export (re-exports every subpath)                     |
| `@agg-build/ui/primitives`   | Buttons, Modal, Chart, Tooltip, etc.                       |
| `@agg-build/ui/events`       | `EventList`, `EventListItem`, `MarketDetails`, `Orderbook` |
| `@agg-build/ui/trading`      | `PlaceOrder`, `Settlement`, trading context utilities      |
| `@agg-build/ui/pages`        | `HomePage`, `EventMarketPage`, `UserProfilePage`           |
| `@agg-build/ui/modals`       | Deposit/Withdraw/Onboarding/Profile/GeoBlock modals        |
| `@agg-build/ui/styles.css`   | Default theme stylesheet (import once at app entry)        |
| `@agg-build/ui/tailwind.css` | Tailwind preset if you run Tailwind v4 yourself            |

## Peer dependencies

- `@agg-build/sdk`
- `@agg-build/hooks`
- `react` ^18.0.0 or ^19.0.0
- `react-dom` ^18.0.0 or ^19.0.0
- `liveline` ^0.0.7 — charting engine used by `Chart` / `LineChart`

No wallet packages required. Wallet-backed auth lives in
[`@agg-build/auth`](https://www.npmjs.com/package/@agg-build/auth).

## Links

- Documentation — <https://docs.agg.market/>
- Storybook — <https://storybook.agg.market/>
- Demo app — <https://demo.agg.market/>
- Vanilla SDK — [`@agg-build/sdk`](https://www.npmjs.com/package/@agg-build/sdk)
- React hooks — [`@agg-build/hooks`](https://www.npmjs.com/package/@agg-build/hooks)
- Connect / sign-in UX — [`@agg-build/auth`](https://www.npmjs.com/package/@agg-build/auth)

## License

MIT
