Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions .claude/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
# Architecture overview

This document is written for Claude Code. It explains the mental model needed to
work in this repository without asking questions about "where does X live" or
"what drives Y".

---

## The core idea in one paragraph

Krateo frontend is a **configuration-driven shell**. There is almost no hardcoded
UI. Instead, the backend serves Kubernetes Custom Resources (CRs) that describe
*what widgets to render, with what data, and with what actions*. The frontend
fetches those resolved CRs at runtime (it fetches them from the backend who resolves the `resourceRefId` references), and
renders the matching React components. Adding a new page means creating YAML in
Kubernetes — no frontend deploy needed.

---

## Startup sequence

```
Browser loads index.html
└─ React app boots, reads public/config/config.json
└─ Fetches INIT endpoint (NavMenu CR from k8s)
└─ Resolves sidebar items → NavMenuItem[] → Route[]
└─ Each Route points to a Page CR
└─ Page CR contains items[] → widget CRs
└─ Each widget CR is rendered by its matching React component
```

`public/config/config.json` is **injected at deploy time** and never committed.
Its keys (`AUTHN_API_BASE_URL`, `SNOWPLOW_API_BASE_URL`, `EVENTS_API_BASE_URL`, …)
are the only place API hostnames appear. Never hardcode URLs.

---

## Source tree

```
src/
components/ shared, reusable UI components
examples/
widgets/ YAML example files — one per widget type
pages/ route-level components
hooks/ custom React hooks
utils/ pure helpers
scripts/ Node/tsx tooling (generate-crds, apply-k8s-yamls…)
docs/ architecture docs, widget guide, ADRs
public/config/ runtime config.json (API base URLs, injected at deploy)
```

---

## The widget system

### Every widget has four artifacts

| Artifact | Location | Purpose |
|---|---|---|
| JSON Schema | `src/widgets/<Name>/<Name>.schema.json` | Single source of truth for the widget's props |
| TypeScript type | `src/widgets/<Name>/<Name>.d.ts` | Auto-generated — never edit |
| YAML example | `src/examples/widgets/<Name>/<Name>.example.yaml` | Applied to k8s for local dev |
| React component | `src/widgets/<Name>/<Name>.tsx` | The renderer |

The schema is the canonical contract. The TypeScript type is derived from it via
`npm run generate-types`. If you change a prop, change the schema first, then
regenerate.

### How a widget CR is resolved at runtime

1. The parent CR contains `items[].resourceRefId: "my-button"`.
2. The frontend calls `GET /call?resource=buttons&name=my-button&namespace=krateo-system`.
3. The response is the Button CR's `.status` object.
4. The Button React component receives that status as props and renders.

`resourceRefId` is always a Kubernetes resource name. The widget kind determines the API resource path
(e.g. `buttons`, `forms`, `tables`).

### Widget categories

**Leaf widgets** (render data, no children):
`BarChart`, `Button`, `EventList`, `LineChart`, `Markdown`, `Paragraph`,
`PieChart`, `YamlViewer`

**Container widgets** (have `items[]` or `allowedResources`, render children):
`ButtonGroup`, `Column`, `DataGrid`, `FlowChart`, `NavMenu`, `Page`,
`Panel`, `Row`, `TabList`

**Action-capable widgets** (have an `actions` object with `rest`/`navigate`/`openDrawer`/`openModal`):
`Button`, `Form`, `Panel`, `Table`

**Infrastructure widgets** (configure the app, not visible to end users):
`NavMenu`, `NavMenuItem`, `Notifications`, `Route`, `RoutesLoader`, `Theme`

### The actions system

Widgets that can trigger side effects declare an `actions` object with four
possible action types:

- `rest` — HTTP call to a Kubernetes endpoint. Supports `requireConfirmation`,
`successMessage`, `errorMessage`, `onSuccessNavigateTo`, `onEventNavigateTo`.
- `navigate` — client-side React Router navigation to a `path` or `resourceRefId`.
- `openDrawer` — opens a side drawer rendering a referenced CR.
- `openModal` — opens a modal dialog rendering a referenced CR.

`clickActionId` on a Button/Panel refers to the `id` of one of these
actions by name. This is the indirection that lets YAML authors wire up
interactions without touching frontend code.

### Server-Sent Events

`EventList` and `Notifications` consume SSE streams. The connection is managed
with `event-source-polyfill`. Topics are passed as widget props (`sseEndpoint`,
`sseTopic`). Never open raw `EventSource` connections outside these two
components.

---

## State management

| Layer | Tool | What lives here |
|---|---|---|
| Server state | TanStack Query v5 | All CRs fetched from k8s, cached, invalidated |
| Local UI state | `useState` | Form field values, modal open, hover |

There is no global store for server data. If you find yourself putting API
responses into Redux, that is wrong — use TanStack Query.

---

## Routing

React Router v7. Routes are **dynamic** — they come from `Route` CRs fetched at
startup, not from a static route config file. The `RoutesLoader` widget is
responsible for fetching and registering them.

For navigation between pages: use React Router's `<Link>` or `useNavigate`.
Never manipulate `window.location` directly.

---

## Styling

CSS Modules only. Every component has a co-located `ComponentName.module.css`.
Class names are `camelCase` in the CSS file and accessed as
`styles.myClassName` in TSX.

No Tailwind. No styled-components. No inline `style={{}}` props unless a value
is truly dynamic (e.g. computed pixel width).

Theming is done via the `Theme` widget, which injects Ant Design design tokens
at runtime. The `mode` field on the Theme CR switches between light and dark.

---

## Charts and graphs

- Time-series / categorical charts → `echarts-for-react` (BarChart, LineChart, PieChart)
- Kubernetes composition graphs → `reactflow` + `@dagrejs/dagre` for layout (FlowChart)

Do not introduce other charting libraries.

---

## Icons

Font Awesome via `@fortawesome/react-fontawesome`. Widget YAML specifies icon
names as strings like `"fa-inbox"` (Font Awesome class name format). The React
components convert this to the correct FA library import. Ant Design
icons are used for some instances (e.g. loading spinner).

---

## Build and tooling

```bash
npm run dev # start dev server on :4000
npm run lint # ESLint (flat config)
npm run lint:css # Stylelint on CSS modules
npm run generate-types # generates a TypeScript type from a JSON schema file (requires json-schema-to-typescript)
npm run generate-crds # regenerate CRDs from JSON schemas (requires krateoctl)
npm run apply-all # apply all k8s YAMLs to current cluster context
npm run validate-schemas # validates JSON schemas
npm run update-readme-widgets # updates the docs/widget-api-reference.md page with specs for each widget
npm run apply-examples # apply all k8s YAMLs from the src/examples folder in the current cluster
npm run apply-crds # apply all k8s YAMLs of generated CRDs from the krateoctl output folder in the current cluster
npm run examples # start dev server and applies k8s YAMLs from the src/examples folder
```

---

## What Claude should NOT do in this repo

- **Do not hardcode API base URLs.** Always read from `public/config/config.json`
via the existing config hook.
- **Do not add new charting libraries.** Use echarts-for-react or reactflow.
- **Do not write global CSS.** Co-locate CSS Modules with the component.
- **Do not run `npm run apply-all` or `npm run generate-crds` without asking.**
These commands write to a live Kubernetes cluster.
107 changes: 107 additions & 0 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Krateo Frontend — Claude Code instructions

## Project overview

This is the **Krateo Platform frontend** — a React 19 + TypeScript SPA that renders
a dynamic UI by fetching widget definitions from Kubernetes CRDs at runtime.
Widgets are defined as JSON Schema files, each in its own folder (which follows the path
`src/widgets/<name>/` and contains the JSON schema, the `.tsx` implementation and the `.ts` type) and consumed
via a Kubernetes-backed API (snowplow, authn, events services).

The JSON schema is used as basis for creating a Kubernetes CRD using `krateoctl`.

Stack: **React 19 · TypeScript 5 · Vite 6 · Ant Design 5 · TanStack Query v5 ·
React Router v7 · Vitest · ESLint 9 (flat config) · Stylelint · Docker / nginx**

## Repository layout

```
src/
components/ shared, reusable UI components
examples/
widgets/ YAML example files — one per widget type
pages/ route-level components
hooks/ custom React hooks
utils/ pure helpers
scripts/ Node/tsx tooling (generate-crds, apply-k8s-yamls…)
docs/ architecture docs, widget guide, ADRs
public/config/ runtime config.json (API base URLs, injected at deploy)
```

## Build and test commands

```bash
npm run dev # start dev server on :4000
npm run lint # ESLint (flat config)
npm run lint:css # Stylelint on CSS modules
npm run generate-types # generates a TypeScript type from a JSON schema file (requires json-schema-to-typescript)
npm run generate-crds # regenerate CRDs from JSON schemas (requires krateoctl)
npm run apply-all # apply all k8s YAMLs to current cluster context
npm run validate-schemas # validates JSON schemas
npm run update-readme-widgets # updates the docs/widget-api-reference.md page with specs for each widget
npm run apply-examples # apply all k8s YAMLs from the src/examples folder in the current cluster
npm run apply-crds # apply all k8s YAMLs of generated CRDs from the krateoctl output folder in the current cluster
npm run examples # start dev server and applies k8s YAMLs from the src/examples folder
```

**Always run `npm run lint` before proposing a PR.**
Never start the dev server unless explicitly asked.

## Code conventions

- All components are **functional** with hooks — no class components.
- CSS is **CSS Modules** (`Component.module.css`) — no inline styles, no Tailwind.
- Imports are sorted: external → internal → relative (enforced by ESLint `import-x`).
- Destructure keys are sorted (enforced by `sort-destructure-keys`).
- Use **TanStack Query** for all server state; never raw `useEffect` + `fetch`.
- Forms use **Ant Design Form** (`antd`) — not React Hook Form.
- Routing is React Router v7 — use `<Link>`, `useNavigate`, `useParams`.
- Charts are rendered with **echarts-for-react**.
- Flow diagrams use **reactflow**.
- Icons come from **@fortawesome/react-fontawesome** — do not import from antd icons.

## Widget system

Widgets are the core abstraction. Each widget:
1. Has a JSON schema in `src/widgets/<name>.schema.json`.
2. Has a YAML example in `src/examples/widgets/<name>.example.yaml` with an example page in `src/examples/widgets/<name>.menu.yaml`.
3. Gets a generated TypeScript type via `npm run generate-types`.
4. Gets a generated CRD via `npm run generate-crds`.
5. Has a React component definition in `src/widgets/<name>.tsx`
6. Has a Typescript file exporting the component in `src/widgets/<name>.ts`
7. If needed, has a custom CSS module in `src/widgets/<name>.module.css`

When creating a new widget, always create all artifacts together.
See `docs/WIDGET-GUIDE.md` for the full step-by-step guide.

## Runtime configuration

The app reads `public/config/config.json` at startup — never hardcode API URLs.
For local dev, copy `public/config/config.example.json` → `public/config/config.json`
and set the correct `AUTHN_API_BASE_URL`, `SNOWPLOW_API_BASE_URL`, `EVENTS_API_BASE_URL`.

## Security boundaries

- **Never edit** `.github/workflows/` without explicit user approval.
- Treat `.env*` files as read-only — never write secrets.
- The `scripts/` directory runs against a live cluster — always confirm before running.
- Do not commit `public/config/config.json` (it is gitignored).

## TypeScript rules

- `strict: true` is enforced — no `any` without a comment justifying it.
- Prefer `unknown` over `any` for external data.
- Keep types co-located with the component unless shared across 3+ files, then move to `src/types/`.

## GitHub integration

When working on issues or PRs, prefer using the `gh-issue-handler` subagent.
Use conventional commit messages: `feat:`, `fix:`, `docs:`, `chore:`, `refactor:`.

## Useful references

- Widget API reference: `docs/widgets-api-reference.md`
- Architecture overview: `docs/ARCHITECTURE.md`
- Widget creation guide: `docs/WIDGET-GUIDE.md`
- Architectural decisions: `docs/DECISIONS.md`
- Krateo docs: https://docs.krateo.io
Loading
Loading