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
11 changes: 11 additions & 0 deletions specs/product/specs.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ Includes:
7. Generic sales orders + FIFO egg-lot allocation + sale decrements stock.
8. Restricted egg lots and sale blocking (medication-withdrawal flag on lot). Already built; keep.
9. Read/list surfaces for daily entries, current stock by grade, and orders.
10. **Web client (React + Vite SPA)** in `web/`, consuming the JSON API: login, daily-entry capture, stock view, customers, sales order (create → add items → confirm), and the list/history screens. This is what makes 1.0 shippable to a farmer — the API alone is not the product.

Explicitly **not** in 1.0 (moved down): payments, dashboard, core reports, expenses, feed/water tracking, mortality movement generation, audit-log UI, CSV export, inventory movement ledger, offline queue. Schema may reserve columns/paths, but no logic is built.

Expand Down Expand Up @@ -620,6 +621,16 @@ Sprints A–D deliver **Phase 1.0 (MVP)** and are the ship gate. Everything afte
- Restricted-lot sale blocking (medication withdrawal) — **built**
- Payments — deferred (possibly indefinitely)

## Sprint UI — Web client (MVP) — not started

React + Vite SPA in `web/`, consuming the JSON API. Runs alongside the backend sprints; each screen lands once its API slice exists.

- SPA scaffold + auth (login, token storage/refresh) + API client + routing shell — **missing**
- Daily-entry capture screen (production by grade) — **missing** (needs Sprint B)
- Stock-by-grade view — **missing** (needs Sprint C)
- Customers + sales order flow (create → add items → confirm) — **missing** (needs Sprint D)
- List/history screens (daily entries, orders) — **missing** (needs read endpoints)

**↑ Ship Phase 1.0 here. ↓ Everything below is post-MVP.**

## Sprint E — Phase 1.1 operational fill
Expand Down
7 changes: 7 additions & 0 deletions web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
dist
dist-ssr
*.local
.env
.vite
*.tsbuildinfo
51 changes: 51 additions & 0 deletions web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Cluckwork Web

React + Vite + TypeScript SPA for Cluckwork, consuming the JSON API
(`Cluckwork.Api`). Phase 1.0 MVP client.

## Stack

- React 19 + React Router 7
- Vite 6 (dev server + build)
- TypeScript (strict)
- No CSS framework yet — plain CSS in `src/styles.css`

## Running

```bash
cp .env.example .env # set VITE_API_TARGET if the API is not on :8080
npm install
npm run dev # http://localhost:5173
```

The dev server proxies `/api/*` to the backend (see `vite.config.ts`), so the
SPA is same-origin with the API and needs no CORS config. Point
`VITE_API_TARGET` at wherever the API runs (docker-compose: `:8080`;
`dotnet run`: the `ASPNETCORE_URLS` port).

You need a user to log in with — the API seeds a default admin on startup once
[#5](../specs/product/specs.md) lands. Until then, create one via the
integration-test path or Swagger.

## Scripts

- `npm run dev` — dev server
- `npm run build` — typecheck + production build to `dist/`
- `npm run typecheck` — types only
- `npm run preview` — serve the built bundle

## Structure

```
src/
api/ fetch client (auth header + transparent refresh) + types
auth/ token store, AuthContext, useAuth
routes/ Login, ProtectedRoute, AppLayout shell, screens
```

## Auth flow

`login` → `POST /api/v1/auth/login` → tokens in localStorage. Authenticated
requests attach the bearer token; a 401 triggers one transparent
`POST /api/v1/auth/refresh` + retry (single-flight). If refresh fails, tokens
clear and the router bounces to `/login`.
12 changes: 12 additions & 0 deletions web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cluckwork</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading
Loading