chore: clean up and restructure repository
This commit is contained in:
193
README.md
193
README.md
@@ -2,38 +2,99 @@
|
||||
|
||||
AI-powered hiring for event and hospitality staffing — a standalone demo build of KROW Forge.
|
||||
|
||||
Runs with no backend, no API keys, and no login. `npm install && npm run dev` and the app is fully
|
||||
Runs with no backend, no API keys and no real login. `npm install && npm run dev` and the app is fully
|
||||
populated.
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
npm run dev # http://localhost:5173 — opens on /admin/login
|
||||
```
|
||||
|
||||
## Architecture
|
||||
## Stack
|
||||
|
||||
Vite + React 18, React Router 6, React Query 5, Tailwind + shadcn/ui, Framer Motion, Recharts.
|
||||
Vite 6 · React 19 · React Router 6 · React Query 5 · Tailwind + shadcn/ui · Framer Motion ·
|
||||
Recharts + MUI X Charts · react-hot-toast.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
src/
|
||||
api/ data client — entity store, demo dataset, local AI engine
|
||||
assets/ brand asset references
|
||||
main.jsx entry — mounts <App/>, imports index.css
|
||||
App.jsx providers + the entire route table
|
||||
index.css design tokens (HSL CSS variables) + utility layers
|
||||
|
||||
api/ the backend seam — see below
|
||||
base44Client.js the contract the production app talks to
|
||||
store.js in-memory entity store, mirrored to localStorage
|
||||
aiEngine.js local, deterministic InvokeLLM
|
||||
seed.js demo dataset
|
||||
attendanceSeed.js shift/attendance dataset
|
||||
|
||||
lib/ domain logic and data hooks
|
||||
krowHooks.js React Query hooks over the entity store
|
||||
krowAi.js AI workflows
|
||||
krowScore.js score engine
|
||||
AuthContext.jsx auth provider
|
||||
admin/ admin session, permissions, position insights
|
||||
skills/ the Owliver skill system (registry, resolver, flows)
|
||||
agents/ the agent system (registry, runtime, lifecycle)
|
||||
|
||||
skills/ *.md skill definitions — loaded by import.meta.glob
|
||||
agents/ *.md agent definitions — loaded by import.meta.glob
|
||||
|
||||
components/
|
||||
ui/ 50 shadcn primitives (design system)
|
||||
krow/ domain components
|
||||
talent/ talent-portal components
|
||||
proving/ Proving Ground challenge components
|
||||
hooks/ shared hooks
|
||||
layouts/ Layout — header, nav pill, control bar, mobile sheet
|
||||
lib/ data hooks, score engine, AI workflows, auth context, utils
|
||||
pages/ 18 route components
|
||||
utils/ helpers
|
||||
ui/ 23 shadcn primitives
|
||||
ds/ 30 design-system components (the app's real vocabulary)
|
||||
admin/ admin page shell + role glyphs
|
||||
krow/ domain components (+ talent/, proving/)
|
||||
charts/ MUI X Charts wrappers and theme
|
||||
agents/ agent configure / canvas / test panel
|
||||
forge/ KROW Forge skill + challenge components
|
||||
skills/ skill authoring surfaces
|
||||
ai-assistant/ Owliver — panel, routing, capabilities, history
|
||||
|
||||
layouts/ AdminLayout — header nav, ⌘K search, assistant panel
|
||||
pages/ route components (admin/ holds the live product)
|
||||
hooks/ shared hooks
|
||||
assets/ brand asset references
|
||||
```
|
||||
|
||||
### The backend seam
|
||||
## Routes
|
||||
|
||||
The production app talks to a Base44 backend through `src/api/base44Client.js`. This demo keeps
|
||||
that module path, export name, and full method contract, and swaps only the transport:
|
||||
The app opens on **`/admin/login`**. Everything else lives under `/admin/*` behind two guards:
|
||||
`ProtectedRoute` (auth) and `AdminRoute` (admin session), rendered inside `AdminLayout`.
|
||||
|
||||
| Path | Page |
|
||||
| --- | --- |
|
||||
| `/admin/login` | Admin sign-in — outside the protected boundary |
|
||||
| `/admin` | Control Center |
|
||||
| `/admin/positions`, `positions/new`, `positions/:id` | Positions, Create, Detail |
|
||||
| `/admin/candidates`, `candidates/:id` | Candidates, Candidate Intelligence profile |
|
||||
| `/admin/hired` · `/admin/talent-pool` | Hired History, Talent Pool |
|
||||
| `/admin/university`, `university/:id` | KROW Forge, challenge detail |
|
||||
| `/admin/analytics` · `/admin/activity` | Analytics, Activity |
|
||||
| `/admin/profile` · `/admin/settings` | Profile, Settings |
|
||||
| `/admin/workspace` | Workspace hub |
|
||||
| `/admin/workspace/agents`, `agents/new`, `agents/:id` | Agent list, agent editor |
|
||||
| `/admin/workspace/skills`, `skills/new`, `skills/:id` | UI skill list and editor |
|
||||
| `/admin/workspace/skills/owliver/new`, `owliver/:id` | Owliver skill editor |
|
||||
| `/admin/workspace/skill-development` | Skill Development |
|
||||
| `*` | `lib/PageNotFound.jsx` |
|
||||
|
||||
Legacy top-level paths (`/overview`, `/positions`, `/candidates`, `/hired`, `/talent-pool`,
|
||||
`/university`, `/analytics`, `/activity`, `/tracking`) all `Navigate` into their `/admin/*`
|
||||
equivalent, id preserved. Static route segments are declared before dynamic ones so
|
||||
`workspace/agents/new` cannot be read as an agent whose id is `"new"`.
|
||||
|
||||
> **Note.** `App.jsx` still imports a set of pre-redesign Employer/Talent pages
|
||||
> (`Overview`, `Apply`, `WorkerProfile`, `KrowIdentity`, `EmployeeDashboard`, `DesignSystem`, …)
|
||||
> and `layouts/Layout.jsx`. None of them are mounted on a route — they are unreachable at runtime
|
||||
> and retained pending a decision on whether to re-route or remove them.
|
||||
|
||||
## The backend seam
|
||||
|
||||
The production app talks to a Base44 backend through `src/api/base44Client.js`. This demo keeps that
|
||||
module path, export name and full method contract, and swaps only the transport:
|
||||
|
||||
| Contract | Demo implementation |
|
||||
| --- | --- |
|
||||
@@ -43,56 +104,48 @@ that module path, export name, and full method contract, and swaps only the tran
|
||||
| `base44.auth.*` | seeded employer/admin user, always signed in |
|
||||
|
||||
Because the seam did not move, everything above it — `lib/krowHooks.js`, `lib/krowAi.js`,
|
||||
`lib/provingGround.js`, `lib/krowScore.js`, every page and component — is unchanged from the
|
||||
reference implementation.
|
||||
`lib/krowScore.js`, every page and component — is unchanged from the reference implementation.
|
||||
|
||||
### The AI engine
|
||||
## The AI engine
|
||||
|
||||
Every AI workflow in the app funnels through one call, `InvokeLLM({ prompt, response_json_schema })`.
|
||||
`api/aiEngine.js` reimplements that function locally. It recognizes each workflow by the phrase its
|
||||
prompt opens with, reads the structured fields the prompt already carries (`Years Experience: 6`,
|
||||
`Required Certifications: …`, the talent-pool JSON block), and scores them deterministically.
|
||||
Every AI workflow funnels through one call, `InvokeLLM({ prompt, response_json_schema })`.
|
||||
`api/aiEngine.js` reimplements it locally: it recognises each workflow by the phrase its prompt opens
|
||||
with, reads the structured fields the prompt already carries (`Years Experience: 6`,
|
||||
`Required Certifications: …`, the talent-pool JSON block) and scores them deterministically.
|
||||
|
||||
Nine workflows are implemented: candidate screening, job-description generation, résumé building,
|
||||
interview questions, interview evaluation, talent matching, Owliver's conversation, Career DNA
|
||||
building, and Proving Ground evaluation.
|
||||
building and Proving Ground evaluation. Results respond to the data rather than being canned — the
|
||||
same input always returns the same output.
|
||||
|
||||
Results respond to the data rather than being canned — screening a 7-year native-English candidate
|
||||
and a 1-year candidate against the same posting returns genuinely different scores, breakdowns,
|
||||
strengths and gaps. The same input always returns the same output.
|
||||
## Skills and agents
|
||||
|
||||
### Owliver
|
||||
Two registries discover their definitions from markdown at build time:
|
||||
|
||||
Two assistants share one visual language:
|
||||
| Registry | Glob | Files |
|
||||
| --- | --- | --- |
|
||||
| `lib/skills/registry.js` | `import.meta.glob('/src/skills/**/*.md')` | 18 Owliver skills, 5 workforce training skills |
|
||||
| `lib/agents/registry.js` | `import.meta.glob('/src/agents/**/*.md')` | 9 agent definitions |
|
||||
|
||||
- **`OwliverAssistant`** — the hiring assistant, mounted on **Overview**, **Candidates** and
|
||||
**Analytics** only. Answers from the same React Query caches the pages render from, so what it
|
||||
says and what the dashboard shows can never disagree.
|
||||
- **`OwliverChatBubble`** — the talent-facing profile builder, mounted in the Talent role.
|
||||
**These globs are absolute paths.** Moving or renaming `src/skills/` or `src/agents/` makes the
|
||||
registry silently return nothing — no build error, no import failure, just an empty registry. Add
|
||||
definitions by dropping a new `.md` file into the right folder; nothing else needs to change.
|
||||
|
||||
## Demo data
|
||||
|
||||
Seeded in `src/api/seed.js`: 7 positions (5 open), 22 applicants, 9 AI-scored candidates averaging
|
||||
76, 4 AI interviews, 3 hires, 6 talent-pool profiles, 4 Proving Ground courses, and an activity log.
|
||||
Seeded in `src/api/seed.js` — positions, applicants, AI-scored candidates, interviews, hires,
|
||||
talent-pool profiles, Proving Ground courses and an activity log; shift data in
|
||||
`src/api/attendanceSeed.js`.
|
||||
|
||||
Edits persist to `localStorage` — screen candidates, hire someone, create a position, and the
|
||||
numbers move everywhere. To restore the shipped dataset:
|
||||
Edits persist to `localStorage`. To restore the shipped dataset:
|
||||
|
||||
```js
|
||||
// in the browser console
|
||||
localStorage.removeItem('krow_demo_db'); location.reload();
|
||||
```
|
||||
|
||||
Bumping `STORAGE_VERSION` in `src/api/store.js` invalidates saved snapshots automatically, so
|
||||
changing the seed does not require a manual clear.
|
||||
|
||||
## Roles
|
||||
|
||||
The header role switcher moves between three surfaces, persisted in `localStorage`:
|
||||
|
||||
- **Employer** — Overview, Positions, Candidates, Hired History, Talent Pool, Analytics
|
||||
- **Admin** — adds Control Center, KROW Forge, Activity
|
||||
- **Talent** — My Portal, KROW Forge, Opportunities
|
||||
Bumping `STORAGE_VERSION` in `src/api/store.js` (currently `8`) invalidates saved snapshots
|
||||
automatically, so changing the seed does not require a manual clear.
|
||||
|
||||
## Design system
|
||||
|
||||
@@ -109,15 +162,49 @@ Tokens live in `src/index.css` as HSL CSS variables consumed by `tailwind.config
|
||||
| Headings | Sora |
|
||||
| Body | Inter |
|
||||
|
||||
The page background is `bg-[#F8FAFC]` under the `gradient-mesh` utility — four radial gradients in
|
||||
blue, yellow and mint. Surfaces use `glass` and `glass-card` (backdrop blur with saturation).
|
||||
`src/components/ds/` is the vocabulary the app actually writes in — `Surface`, `KpiCard`,
|
||||
`DataTable`, `PageHeader`, `Modal`, `toast` and the rest, exported through `ds/index.js`.
|
||||
`src/components/ui/` holds the shadcn primitives those are built on. Surfaces use `glass` and
|
||||
`glass-card`; the page background is the `gradient-mesh` utility.
|
||||
|
||||
Toasts go through `ds/toast` (a wrapper over `react-hot-toast`), rendered by `<HotToaster>` in
|
||||
`App.jsx`.
|
||||
|
||||
## Scripts
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `npm run dev` | Dev server (honors `PORT`) |
|
||||
| `npm run dev` | Dev server (honors `PORT`, default 5173) |
|
||||
| `npm run build` | Production build |
|
||||
| `npm run preview` | Serve the build |
|
||||
| `npm run lint` | ESLint |
|
||||
| `npm run lint` | ESLint — currently clean |
|
||||
| `npm run lint:fix` | ESLint with `--fix` |
|
||||
| `npm test` | `scripts/skill-check.mjs` — 835 assertions over the skill and agent systems |
|
||||
| `npm run typecheck` | `tsc -p ./jsconfig.json` with `checkJs` |
|
||||
|
||||
### The test harness
|
||||
|
||||
`npm test` boots a real Vite dev server in middleware mode and `ssrLoadModule`s the actual
|
||||
application modules, so it exercises the real `@/` alias and `import.meta.glob` pipeline rather than
|
||||
mocks. It also diffs Owliver's behaviour across eleven page contexts against a committed snapshot,
|
||||
`scripts/__baseline__/owliver-baseline.json`; regenerate that deliberately with
|
||||
`node scripts/owliver-baseline.mjs --write`, never to turn a red check green.
|
||||
|
||||
Because it loads modules by absolute path, the harness pins the locations of `src/api/`, `src/lib/`,
|
||||
`src/lib/skills/`, `src/lib/agents/`, `src/components/ai-assistant/`, `src/App.jsx`,
|
||||
`src/layouts/AdminLayout.jsx` and a handful of agent files. Move any of them and update
|
||||
`scripts/owliver-capture.mjs` and `scripts/skill-check.mjs` in the same change.
|
||||
|
||||
### Known-failing checks
|
||||
|
||||
Two checks fail on a clean checkout and are tracked as separate work, not regressions:
|
||||
|
||||
- `npm test` — 834/835 pass; `the seeded overtime climb is found` fails.
|
||||
- `npm run typecheck` — 59 errors, all pre-existing JSDoc/inference gaps in `lib/krowAi.js`,
|
||||
`lib/skills/*`, `lib/positionModel.js` and a few components.
|
||||
|
||||
## Deployment
|
||||
|
||||
`Dockerfile` builds with `node:22-alpine` (capped at 1 GB heap for small hosts) and serves the
|
||||
static `dist/` from `nginx:alpine` using `nginx.conf` — gzip, immutable `/assets/` caching and an
|
||||
SPA fallback to `index.html` so client-side routes resolve on refresh.
|
||||
|
||||
Reference in New Issue
Block a user