diff --git a/.gitignore b/.gitignore index 376a232..13c8720 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,6 @@ venv/ env/ # Django -backend/db.sqlite3 backend/media/ backend/staticfiles/ diff --git a/README.md b/README.md index d1aa716..3fa4520 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,84 @@ # Django Port Workspace This folder contains the rewrite target for the current FastAPI + React application. +It is intended to be self-contained. ## What is here -- [`backend`](/home/sandy/HUB-master/django-port/backend): Django project and domain apps -- [`frontend`](/home/sandy/HUB-master/django-port/frontend): SvelteKit frontend prepared for `shadcn-svelte` -- [`docs/port-plan.md`](/home/sandy/HUB-master/django-port/docs/port-plan.md): migration strategy and scope +- [`backend`](backend): Django project and domain apps +- [`frontend`](frontend): SvelteKit frontend with a local manual shadcn-style component layer +- [`data/legacy`](data/legacy): bundled legacy SQLite sources used by `import_legacy_data` +- [`docs/port-plan.md`](docs/port-plan.md): migration strategy and scope + +## Self-contained data + +The folder already contains: + +- the live Django database at [`backend/db.sqlite3`](backend/db.sqlite3) +- the bundled legacy import sources at [`data/legacy/cincin_phase1.sqlite`](data/legacy/cincin_phase1.sqlite) and [`data/legacy/dalcorso.sqlite`](data/legacy/dalcorso.sqlite) + +That means `django-port` no longer depends on the repo-root legacy databases for normal use or for re-running the importer. + +## Fresh Machine Setup + +`django-port` is now structured so it can be pushed as its own repository and used without the rest of the original workspace. + +Backend setup: + +```bash +cd backend +python3 -m venv .venv +source .venv/bin/activate +pip install -e . +cp .env.example .env +python manage.py migrate +python manage.py runserver +``` + +Frontend setup: + +```bash +cd frontend +npm install +cp .env.example .env +npm run dev +``` + +Default local URLs: + +- frontend: `http://localhost:5173` +- backend API: `http://localhost:8000/api` + +## Pushing As Its Own Repo + +If this folder is moved into a new repository, include at minimum: + +- `backend/` +- `frontend/` +- `data/legacy/` +- `docs/` +- `.gitignore` +- `README.md` + +If you want the seeded app data on the destination machine, commit and push: + +- [`backend/db.sqlite3`](backend/db.sqlite3) +- [`data/legacy/cincin_phase1.sqlite`](data/legacy/cincin_phase1.sqlite) +- [`data/legacy/dalcorso.sqlite`](data/legacy/dalcorso.sqlite) + +The repo does not need to include: + +- any files outside `django-port` +- a local virtualenv +- `frontend/node_modules` +- `frontend/.svelte-kit` + +## Environment Files + +Example env files are included at: + +- [`backend/.env.example`](backend/.env.example) +- [`frontend/.env.example`](frontend/.env.example) ## What is intentionally not done @@ -14,4 +86,4 @@ This folder contains the rewrite target for the current FastAPI + React applicat - No Svelte or Django bootstrapping commands - No heavy build, dev-server, or migration runs -Everything committed here is code and structure only. +Everything needed for the port itself now lives under this folder. diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..cd7985a --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,10 @@ +DJANGO_SECRET_KEY=change-me +DJANGO_DEBUG=1 +DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1 +DJANGO_TIME_ZONE=Europe/Budapest +DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 +DJANGO_CORS_ALLOWED_ORIGINS=http://localhost:5173,http://127.0.0.1:5173 + +# Optional overrides. The defaults already point at ../data/legacy/. +# LEGACY_CINCIN_DB=/absolute/path/to/cincin_phase1.sqlite +# LEGACY_DALCORSO_DB=/absolute/path/to/dalcorso.sqlite diff --git a/backend/README.md b/backend/README.md index 27648a7..fc98d4d 100644 --- a/backend/README.md +++ b/backend/README.md @@ -14,14 +14,24 @@ This is the replacement backend for the legacy FastAPI app. ## Expected setup -1. Install dependencies from [`pyproject.toml`](/home/sandy/HUB-master/django-port/backend/pyproject.toml). -2. Run `python manage.py makemigrations`. -3. Run `python manage.py migrate`. -4. Run `python manage.py createsuperuser`. -5. Run `python manage.py import_legacy_data`. +1. Create a virtualenv in `backend/`. +2. Install dependencies from [`pyproject.toml`](pyproject.toml) with `pip install -e .`. +3. Copy [`backend/.env.example`](.env.example) to `.env` or export the same variables in your shell. +4. Run `python manage.py migrate`. +5. Run `python manage.py createsuperuser` if you are starting from a blank DB. +6. Run `python manage.py import_legacy_data` if you want to rebuild from the bundled legacy sources instead of using the committed `db.sqlite3`. + +The import command now defaults to the bundled databases inside: + +- [`../data/legacy/cincin_phase1.sqlite`](../data/legacy/cincin_phase1.sqlite) +- [`../data/legacy/dalcorso.sqlite`](../data/legacy/dalcorso.sqlite) + +You can still override them with `LEGACY_CINCIN_DB` and `LEGACY_DALCORSO_DB` if needed. ## Notes - Auth uses Django sessions instead of custom JWT cookies. - The import command consolidates both legacy SQLite files into one Django schema. - Password hashes are not carried over directly; imported users get a forced reset placeholder. +- The backend is intended to be runnable from the `django-port` subtree without depending on workspace-root data files. +- The committed `db.sqlite3` is optional but supported. Keep it in the repo if you want seeded data to travel with the project. diff --git a/backend/config/settings.py b/backend/config/settings.py index 5bca83d..fc032d2 100644 --- a/backend/config/settings.py +++ b/backend/config/settings.py @@ -110,5 +110,6 @@ CORS_ALLOWED_ORIGINS = [ ) ] -LEGACY_CINCIN_DB = Path(os.getenv("LEGACY_CINCIN_DB", BASE_DIR.parent.parent / "cincin_phase1.sqlite")) -LEGACY_DALCORSO_DB = Path(os.getenv("LEGACY_DALCORSO_DB", BASE_DIR.parent.parent / "dalcorso.sqlite")) +LEGACY_DATA_DIR = BASE_DIR.parent / "data" / "legacy" +LEGACY_CINCIN_DB = Path(os.getenv("LEGACY_CINCIN_DB", LEGACY_DATA_DIR / "cincin_phase1.sqlite")) +LEGACY_DALCORSO_DB = Path(os.getenv("LEGACY_DALCORSO_DB", LEGACY_DATA_DIR / "dalcorso.sqlite")) diff --git a/backend/db.sqlite3 b/backend/db.sqlite3 new file mode 100644 index 0000000..7c777e1 Binary files /dev/null and b/backend/db.sqlite3 differ diff --git a/data/legacy/README.md b/data/legacy/README.md new file mode 100644 index 0000000..e6ef49b --- /dev/null +++ b/data/legacy/README.md @@ -0,0 +1,13 @@ +# Bundled Legacy Data + +This directory contains the legacy SQLite databases used by the Django import command: + +- `cincin_phase1.sqlite` +- `dalcorso.sqlite` + +The defaults in [`backend/config/settings.py`](../../backend/config/settings.py) point here so the `django-port` subtree can be used without depending on workspace-root copies of those files. + +If needed, the importer can still be redirected with: + +- `LEGACY_CINCIN_DB` +- `LEGACY_DALCORSO_DB` diff --git a/data/legacy/cincin_phase1.sqlite b/data/legacy/cincin_phase1.sqlite new file mode 100644 index 0000000..e10686e Binary files /dev/null and b/data/legacy/cincin_phase1.sqlite differ diff --git a/data/legacy/dalcorso.sqlite b/data/legacy/dalcorso.sqlite new file mode 100644 index 0000000..0f0e80a Binary files /dev/null and b/data/legacy/dalcorso.sqlite differ diff --git a/docs/port-plan.md b/docs/port-plan.md index cf21fcf..06d381d 100644 --- a/docs/port-plan.md +++ b/docs/port-plan.md @@ -21,7 +21,7 @@ 1. Create Django migrations from the new model set. 2. Migrate the new database. -3. Run `import_legacy_data` to ingest `cincin_phase1.sqlite` and `dalcorso.sqlite`. +3. Run `import_legacy_data` to ingest the bundled legacy SQLite files in `data/legacy/`. 4. Rebuild auth credentials by forcing password resets for imported users. 5. Move ETL scripts into Django management commands later instead of repo-root scripts. diff --git a/docs/session-handoff-2026-04-01-ui-reset.md b/docs/session-handoff-2026-04-01-ui-reset.md new file mode 100644 index 0000000..dccea12 --- /dev/null +++ b/docs/session-handoff-2026-04-01-ui-reset.md @@ -0,0 +1,396 @@ +# Session Handoff + +Date: April 1, 2026 + +Workspace root: [`/home/sandy/HUB-master`](/home/sandy/HUB-master) + +Port root: [`/home/sandy/HUB-master/django-port`](/home/sandy/HUB-master/django-port) + +This document supersedes the earlier handoff for the continuation work done after [`session-handoff-2026-04-01.md`](/home/sandy/HUB-master/django-port/docs/session-handoff-2026-04-01.md). + +## 1. What Happened In This Continuation + +The continuation work completed six major areas: + +1. business access scoping was tightened across the backend API +2. missing user and invoice update/delete flows were added +3. a local `.gitignore` was added for the `django-port` subtree +4. the temporary UI layer was replaced manually with a local `shadcn-svelte` style component system, then restyled again into a strict black-background, white-text, vanilla shadcn look +5. the `django-port` subtree was made self-contained for data and repo portability +6. startup and portability docs were tightened so the folder can be pushed as its own repo and started on another machine with only local env files and dependency installs + +Important note: + +- this UI work does **not** depend on the external `shadcn-svelte` CLI +- the component layer now exists as local source files inside the repo + +## 2. Backend Changes Since The Previous Handoff + +Primary backend file still in play: + +- [`/home/sandy/HUB-master/django-port/backend/apps/api/views.py`](/home/sandy/HUB-master/django-port/backend/apps/api/views.py) + +### 2.1 Business Scoping Hardening + +Scoping helpers were added or expanded so non-superusers now default to business-limited data more consistently: + +- scoped vendors +- scoped categories +- scoped products +- scoped invoices +- vendor access checks +- invoice payload access validation + +Effects: + +- dashboard overview is no longer global for non-superusers +- vendors list now defaults to allowed businesses +- vendor detail is denied outside allowed businesses +- invoices list defaults to allowed businesses +- invoice detail is resolved through scoped querysets +- inventory now derives visibility from scoped products instead of the earlier brittle ad hoc filter +- business summary inventory count is tied to that business’s vendor/category graph instead of the earlier global count + +### 2.2 User And Invoice CRUD Completion + +Invoice service logic was consolidated in: + +- [`/home/sandy/HUB-master/django-port/backend/apps/operations/services.py`](/home/sandy/HUB-master/django-port/backend/apps/operations/services.py) + +The service now has a shared persistence path for create and update: + +- `create_invoice_from_payload` +- `update_invoice_from_payload` + +Added backend routes: + +- `PUT /api/invoices//` +- `DELETE /api/invoices//` +- `GET /api/settings/users//` +- `PUT /api/settings/users//` +- `DELETE /api/settings/users//` + +Routing file: + +- [`/home/sandy/HUB-master/django-port/backend/apps/api/urls.py`](/home/sandy/HUB-master/django-port/backend/apps/api/urls.py) + +### 2.3 Safety Notes + +Current user deletion behavior: + +- users cannot delete their own account through the new user detail endpoint + +Current invoice write validation: + +- business access is checked +- vendor access is checked +- category access is checked for non-superusers +- product access is checked for non-superusers + +## 3. Frontend/API Surface Added Since The Previous Handoff + +API client file: + +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/api/client.ts`](/home/sandy/HUB-master/django-port/frontend/src/lib/api/client.ts) + +Added frontend client methods: + +- `updateInvoice` +- `deleteInvoice` +- `updateUser` +- `deleteUser` + +Frontend pages updated to use those flows: + +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/invoices/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/invoices/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/settings/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/settings/+page.svelte) + +Behavior: + +- invoice page now supports create, edit, and delete +- settings page now supports create, edit, and delete for users + +## 4. Gitignore Added + +Added: + +- [`/home/sandy/HUB-master/django-port/.gitignore`](/home/sandy/HUB-master/django-port/.gitignore) + +It ignores: + +- `venv` +- Python caches +- Django static/media outputs +- `frontend/node_modules` +- Svelte/Vite build artifacts +- common log and editor files + +Important update from the latest continuation: + +- the live Django database is **no longer ignored** +- this was changed intentionally so `backend/db.sqlite3` can be committed if the user wants the shipped data included when pushing `django-port` to another repository + +## 4.1 Self-Contained Data Packaging + +The project no longer depends on workspace-root legacy SQLite files. + +Bundled data now lives inside the port itself: + +- [`/home/sandy/HUB-master/django-port/backend/db.sqlite3`](/home/sandy/HUB-master/django-port/backend/db.sqlite3) +- [`/home/sandy/HUB-master/django-port/data/legacy/cincin_phase1.sqlite`](/home/sandy/HUB-master/django-port/data/legacy/cincin_phase1.sqlite) +- [`/home/sandy/HUB-master/django-port/data/legacy/dalcorso.sqlite`](/home/sandy/HUB-master/django-port/data/legacy/dalcorso.sqlite) + +Defaults in: + +- [`/home/sandy/HUB-master/django-port/backend/config/settings.py`](/home/sandy/HUB-master/django-port/backend/config/settings.py) + +now point `LEGACY_CINCIN_DB` and `LEGACY_DALCORSO_DB` at `django-port/data/legacy/` instead of the workspace root. + +Documentation for this was added/updated in: + +- [`/home/sandy/HUB-master/django-port/README.md`](/home/sandy/HUB-master/django-port/README.md) +- [`/home/sandy/HUB-master/django-port/backend/README.md`](/home/sandy/HUB-master/django-port/backend/README.md) +- [`/home/sandy/HUB-master/django-port/docs/port-plan.md`](/home/sandy/HUB-master/django-port/docs/port-plan.md) +- [`/home/sandy/HUB-master/django-port/data/legacy/README.md`](/home/sandy/HUB-master/django-port/data/legacy/README.md) + +Additional portability setup in the latest pass: + +- [`/home/sandy/HUB-master/django-port/backend/.env.example`](/home/sandy/HUB-master/django-port/backend/.env.example) +- [`/home/sandy/HUB-master/django-port/frontend/.env.example`](/home/sandy/HUB-master/django-port/frontend/.env.example) + +The root README now includes: + +- fresh-machine backend setup +- fresh-machine frontend setup +- repo contents that should be pushed +- explicit note that files outside `django-port` are no longer required + +## 5. Current UI Architecture + +This changed substantially. + +The old situation was: + +- temporary `button.svelte` +- temporary `card.svelte` +- warm custom palette +- mixed per-page raw inputs/selects/textareas + +The current situation is: + +- a local manual `shadcn-svelte` style component layer lives under [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui) +- global styling now uses a dark vanilla shadcn-like token set +- screens have been migrated onto those primitives + +### 5.1 Core UI Files + +Utility helper: + +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/utils.ts`](/home/sandy/HUB-master/django-port/frontend/src/lib/utils.ts) + +Core primitives: + +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/button.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/button.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-header.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-header.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-title.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-title.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-description.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-description.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-content.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-content.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-footer.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/card-footer.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/input.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/input.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/label.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/label.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/select.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/select.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/textarea.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/ui/textarea.svelte) + +Global theme: + +- [`/home/sandy/HUB-master/django-port/frontend/src/app.css`](/home/sandy/HUB-master/django-port/frontend/src/app.css) + +### 5.2 Visual Direction Now In Effect + +The latest user correction explicitly required: + +- white text +- black backgrounds +- purely vanilla shadcn look +- no leftover warm/custom brand styling + +Current global direction: + +- background is near-black +- foreground is near-white +- cards are dark +- primary action is light-on-dark inversion +- muted surfaces are dark zinc-like blocks +- typography uses a plain system sans stack rather than the earlier decorative direction + +### 5.3 Shell Layout + +Shell files: + +- [`/home/sandy/HUB-master/django-port/frontend/src/lib/components/app-shell/sidebar.svelte`](/home/sandy/HUB-master/django-port/frontend/src/lib/components/app-shell/sidebar.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/+layout.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/+layout.svelte) + +Current shell behavior: + +- dark app frame +- left navigation in a sticky card +- active route highlighted +- main content area wrapped in a dark bordered panel + +## 6. Pages Converted To The Manual Shadcn Layer + +The following pages are now on the new local component layer and dark token system: + +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/login/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/login/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/dashboard/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/dashboard/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/invoices/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/invoices/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/vendors/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/vendors/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/inventory/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/inventory/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/events/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/events/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/schedule/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/schedule/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/settings/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/settings/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/devices/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/devices/+page.svelte) +- [`/home/sandy/HUB-master/django-port/frontend/src/routes/app/business/[id]/+page.svelte`](/home/sandy/HUB-master/django-port/frontend/src/routes/app/business/[id]/+page.svelte) + +## 7. Current Quality/Verification State + +Backend verification used: + +```bash +python3 -m compileall django-port/backend +``` + +This passed after the backend continuation work. + +Frontend verification used: + +```bash +npm run check +``` + +This now passes with: + +- `0 errors` +- `0 warnings` + +That is an improvement over the prior state where the frontend still had a set of accessibility warnings and one intermediate TypeScript regression during the CRUD/UI migration. + +## 8. What Is Better Now + +Compared with the prior handoff, the project is in a materially better state: + +- backend scoping is more defensible +- missing user/invoice CRUD is no longer missing +- repo noise in `django-port` is reduced via `.gitignore` +- the data needed to rerun the importer now lives under `django-port` +- the live Django database can now be committed with the subtree for portability +- UI is no longer based on temporary one-off primitives +- the frontend no longer mixes multiple visual directions +- the current visual direction now matches the user’s final explicit correction: black backgrounds, white text, vanilla shadcn feel + +## 9. Remaining Gaps / Risks + +Even after this continuation, there are still real gaps. + +### 9.1 UI Is Manual, Not CLI-Generated + +The component layer is local and manual. + +That is acceptable for the user’s stated direction, but it means: + +- future new components are still hand-authored unless someone later chooses to bring in the CLI workflow + +### 9.2 Some Interaction Primitives Are Still Native + +The visual system is consistent enough now, but some lower-level interaction pieces are still native rather than componentized, for example: + +- plain checkbox controls +- some inline destructive text buttons +- some per-row action buttons inside lists + +If the next session wants to push polish further, likely next UI candidates are: + +- checkbox +- badge +- alert +- table or data-list wrappers +- reusable stat-card component + +### 9.3 API Structure Is Still Large + +This is unchanged from the earlier handoff: + +- [`/home/sandy/HUB-master/django-port/backend/apps/api/views.py`](/home/sandy/HUB-master/django-port/backend/apps/api/views.py) is still too large + +Correctness was prioritized over structural refactor. + +### 9.4 No Automated Backend Test Coverage + +Still missing: + +- API tests +- model/service tests +- end-to-end tests + +### 9.5 Repo Portability Depends On What Gets Committed + +The folder is now prepared so it can stand alone, but another machine still only gets what is actually pushed. + +To make the subtree truly self-contained in a new repository, the pushed repo should include at minimum: + +- `backend/` +- `frontend/` +- `data/legacy/` +- `docs/` +- `.gitignore` +- `README.md` +- `backend/db.sqlite3` if the preloaded application data should ship too + +It should **not** need: + +- repo-root legacy databases outside `django-port` +- repo-root frontend/backend folders outside `django-port` +- local `venv` +- local `node_modules` +- `.svelte-kit` + +Recommended startup files for another machine: + +- `backend/.env` copied from `backend/.env.example` +- `frontend/.env` copied from `frontend/.env.example` + +Only the host/origin values should need adjustment if the deployment URLs differ from the local defaults. + +## 10. Recommended Next Steps + +If another Codex instance continues from here, recommended order is: + +1. do manual browser QA on the dark UI across the main routes +2. fix any remaining visual inconsistencies discovered in that manual pass +3. decide whether `backend/db.sqlite3` should be committed in the destination repo as seeded app data +4. add reusable primitives for checkbox/badge/alert if more UI polish is desired +5. only after UI stabilizes, consider splitting `apps/api/views.py` +6. add targeted API tests for business scoping and user/invoice CRUD + +## 11. Immediate Startup Context For The Next Codex + +The project is no longer at a bootstrap stage. + +The current state should be understood as: + +- Django backend is functioning +- Svelte frontend is functioning +- auth works +- scoping is better than before +- user/invoice CRUD exists +- UI layer is now local manual shadcn-style and dark + +So the next session should not redo scaffolding. + +It should assume the task is now one of: + +- polish +- QA +- test coverage +- structural cleanup diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..f7b7b84 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1 @@ +VITE_API_BASE=http://localhost:8000/api diff --git a/frontend/src/app.css b/frontend/src/app.css index 3fb6701..12fcafb 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -1,38 +1,50 @@ @import "tailwindcss"; :root { - --background: #f5f1e8; - --foreground: #201916; - --card: #fffcf6; - --card-foreground: #201916; - --popover: #fff9ef; - --popover-foreground: #201916; - --primary: #16302b; - --primary-foreground: #f6efe4; - --secondary: #d8c3a8; - --secondary-foreground: #2d241e; - --muted: #ece2d3; - --muted-foreground: #615246; - --accent: #ba6c46; - --accent-foreground: #fff7ef; - --border: #cdbca5; - --input: #e7dbca; - --ring: #16302b; + --background: #09090b; + --foreground: #fafafa; + --card: #09090b; + --card-foreground: #fafafa; + --popover: #09090b; + --popover-foreground: #fafafa; + --primary: #fafafa; + --primary-foreground: #09090b; + --secondary: #18181b; + --secondary-foreground: #fafafa; + --muted: #18181b; + --muted-foreground: #a1a1aa; + --accent: #27272a; + --accent-foreground: #fafafa; + --border: #27272a; + --input: #27272a; + --ring: #d4d4d8; } body { - background: - radial-gradient(circle at top left, rgba(186, 108, 70, 0.18), transparent 30%), - radial-gradient(circle at bottom right, rgba(22, 48, 43, 0.18), transparent 35%), - var(--background); + background: var(--background); color: var(--foreground); - font-family: "IBM Plex Sans", "Segoe UI", sans-serif; + font-family: Arial, Helvetica, sans-serif; min-height: 100vh; } -.panel { - background: color-mix(in srgb, var(--card) 92%, white); - border: 1px solid var(--border); - border-radius: 1rem; - box-shadow: 0 18px 60px rgba(32, 25, 22, 0.08); +* { + border-color: var(--border); + box-sizing: border-box; +} + +::selection { + background: #3f3f46; + color: var(--foreground); +} + +button, +input, +select, +textarea { + font: inherit; +} + +a { + color: inherit; + text-decoration: none; } diff --git a/frontend/src/lib/components/app-shell/sidebar.svelte b/frontend/src/lib/components/app-shell/sidebar.svelte index aa89ecd..3a75586 100644 --- a/frontend/src/lib/components/app-shell/sidebar.svelte +++ b/frontend/src/lib/components/app-shell/sidebar.svelte @@ -1,7 +1,9 @@ - + diff --git a/frontend/src/lib/components/ui/button.svelte b/frontend/src/lib/components/ui/button.svelte index 5bbaf83..1dab41b 100644 --- a/frontend/src/lib/components/ui/button.svelte +++ b/frontend/src/lib/components/ui/button.svelte @@ -1,19 +1,44 @@ + +
+ +
diff --git a/frontend/src/lib/components/ui/card-description.svelte b/frontend/src/lib/components/ui/card-description.svelte new file mode 100644 index 0000000..a6e0e96 --- /dev/null +++ b/frontend/src/lib/components/ui/card-description.svelte @@ -0,0 +1,9 @@ + + +

+ +

diff --git a/frontend/src/lib/components/ui/card-footer.svelte b/frontend/src/lib/components/ui/card-footer.svelte new file mode 100644 index 0000000..257004f --- /dev/null +++ b/frontend/src/lib/components/ui/card-footer.svelte @@ -0,0 +1,9 @@ + + +
+ +
diff --git a/frontend/src/lib/components/ui/card-header.svelte b/frontend/src/lib/components/ui/card-header.svelte new file mode 100644 index 0000000..c498f2d --- /dev/null +++ b/frontend/src/lib/components/ui/card-header.svelte @@ -0,0 +1,9 @@ + + +
+ +
diff --git a/frontend/src/lib/components/ui/card-title.svelte b/frontend/src/lib/components/ui/card-title.svelte new file mode 100644 index 0000000..dbe980d --- /dev/null +++ b/frontend/src/lib/components/ui/card-title.svelte @@ -0,0 +1,9 @@ + + +

+ +

diff --git a/frontend/src/lib/components/ui/card.svelte b/frontend/src/lib/components/ui/card.svelte index 534899c..e441809 100644 --- a/frontend/src/lib/components/ui/card.svelte +++ b/frontend/src/lib/components/ui/card.svelte @@ -1,3 +1,14 @@ -
+ + +
diff --git a/frontend/src/lib/components/ui/input.svelte b/frontend/src/lib/components/ui/input.svelte new file mode 100644 index 0000000..39e2fe2 --- /dev/null +++ b/frontend/src/lib/components/ui/input.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/label.svelte b/frontend/src/lib/components/ui/label.svelte new file mode 100644 index 0000000..9b91424 --- /dev/null +++ b/frontend/src/lib/components/ui/label.svelte @@ -0,0 +1,10 @@ + + + diff --git a/frontend/src/lib/components/ui/select.svelte b/frontend/src/lib/components/ui/select.svelte new file mode 100644 index 0000000..72153b0 --- /dev/null +++ b/frontend/src/lib/components/ui/select.svelte @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/lib/components/ui/textarea.svelte b/frontend/src/lib/components/ui/textarea.svelte new file mode 100644 index 0000000..bd29c12 --- /dev/null +++ b/frontend/src/lib/components/ui/textarea.svelte @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts new file mode 100644 index 0000000..a5ef193 --- /dev/null +++ b/frontend/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/frontend/src/routes/app/+layout.svelte b/frontend/src/routes/app/+layout.svelte index c8748f7..289818e 100644 --- a/frontend/src/routes/app/+layout.svelte +++ b/frontend/src/routes/app/+layout.svelte @@ -2,6 +2,7 @@ import { onMount } from "svelte"; import { goto } from "$app/navigation"; import Sidebar from "$lib/components/app-shell/sidebar.svelte"; + import Card from "$lib/components/ui/card.svelte"; import { authReady, bootstrapAuth } from "$lib/stores/auth"; let loading = true; @@ -24,21 +25,21 @@ {#if loading || !$authReady}
-
+

Session

Checking authentication…

-
+
{:else if error}
-
-

{error}

-
+ +

{error}

+
{:else} -
+
-
+
diff --git a/frontend/src/routes/app/business/[id]/+page.svelte b/frontend/src/routes/app/business/[id]/+page.svelte index 79114d2..fe3f5e3 100644 --- a/frontend/src/routes/app/business/[id]/+page.svelte +++ b/frontend/src/routes/app/business/[id]/+page.svelte @@ -2,6 +2,10 @@ import { onMount } from "svelte"; import { page } from "$app/state"; import Card from "$lib/components/ui/card.svelte"; + import CardContent from "$lib/components/ui/card-content.svelte"; + import CardDescription from "$lib/components/ui/card-description.svelte"; + import CardHeader from "$lib/components/ui/card-header.svelte"; + import CardTitle from "$lib/components/ui/card-title.svelte"; import { api } from "$lib/api/client"; import type { BusinessSummaryPayload } from "$lib/types/domain"; @@ -19,7 +23,7 @@
{#if error} -

{error}

+

{error}

{:else if summary}

{summary.business.short_code}

@@ -27,21 +31,21 @@
-

Revenue

{summary.stats.total_revenue.toFixed(2)} {summary.business.currency}

-

Expenses

{summary.stats.total_expenses.toFixed(2)} {summary.business.currency}

-

Outstanding invoices

{summary.stats.outstanding_invoices}

-

Invoices

{summary.stats.invoice_count}

-

VAT tracked

{summary.stats.total_vat.toFixed(2)} {summary.business.currency}

-

Inventory items

{summary.stats.inventory_items}

+

Revenue

{summary.stats.total_revenue.toFixed(2)} {summary.business.currency}

+

Expenses

{summary.stats.total_expenses.toFixed(2)} {summary.business.currency}

+

Outstanding invoices

{summary.stats.outstanding_invoices}

+

Invoices

{summary.stats.invoice_count}

+

VAT tracked

{summary.stats.total_vat.toFixed(2)} {summary.business.currency}

+

Inventory items

{summary.stats.inventory_items}

-
-

Recent revenue

-

Latest imported revenue summary rows for this business.

-
-
+ + Recent revenue + Latest imported revenue summary rows for this business. + + {#each summary.recent_revenue as row}
@@ -54,15 +58,15 @@
{/each} -
+
-
-

Recent invoices

-

Most recent invoice activity linked to this business.

-
-
+ + Recent invoices + Most recent invoice activity linked to this business. + + {#each summary.recent_invoices as invoice}
@@ -75,10 +79,10 @@
{/each} -
+
{:else} - Loading business summary… + Loading business summary… {/if}
diff --git a/frontend/src/routes/app/dashboard/+page.svelte b/frontend/src/routes/app/dashboard/+page.svelte index c5ee577..2c3e59b 100644 --- a/frontend/src/routes/app/dashboard/+page.svelte +++ b/frontend/src/routes/app/dashboard/+page.svelte @@ -2,6 +2,10 @@ import { onMount } from "svelte"; import Button from "$lib/components/ui/button.svelte"; import Card from "$lib/components/ui/card.svelte"; + import CardContent from "$lib/components/ui/card-content.svelte"; + import CardDescription from "$lib/components/ui/card-description.svelte"; + import CardHeader from "$lib/components/ui/card-header.svelte"; + import CardTitle from "$lib/components/ui/card-title.svelte"; import { api } from "$lib/api/client"; import type { DashboardBusinessSummary, DashboardOverview } from "$lib/types/domain"; @@ -30,27 +34,25 @@
{#if error} -

{error}

+

{error}

{:else if overview}
-

Revenue

{overview.total_revenue.toFixed(2)}

-

Expenses

{overview.total_expenses.toFixed(2)}

-

Outstanding invoices

{overview.outstanding_invoices}

-

VAT tracked

{overview.total_vat.toFixed(2)}

-

Vendors

{overview.vendor_count}

-

Unread notifications

{overview.unread_notifications}

+

Revenue

{overview.total_revenue.toFixed(2)}

+

Expenses

{overview.total_expenses.toFixed(2)}

+

Outstanding invoices

{overview.outstanding_invoices}

+

VAT tracked

{overview.total_vat.toFixed(2)}

+

Vendors

{overview.vendor_count}

+

Unread notifications

{overview.unread_notifications}

-
-
-

Business roll-up

-

Per-business revenue, expenses, and invoice pressure.

-
-
-
+ + Business roll-up + Per-business revenue, expenses, and invoice pressure. + + {#each businesses as business} -
+

{business.business_name}

@@ -66,9 +68,9 @@
{/each} -
+ {:else} - Loading dashboard… + Loading dashboard… {/if}
diff --git a/frontend/src/routes/app/devices/+page.svelte b/frontend/src/routes/app/devices/+page.svelte index 17af2e6..9701763 100644 --- a/frontend/src/routes/app/devices/+page.svelte +++ b/frontend/src/routes/app/devices/+page.svelte @@ -2,6 +2,11 @@ import { onMount } from "svelte"; import Button from "$lib/components/ui/button.svelte"; import Card from "$lib/components/ui/card.svelte"; + import CardContent from "$lib/components/ui/card-content.svelte"; + import CardDescription from "$lib/components/ui/card-description.svelte"; + import CardHeader from "$lib/components/ui/card-header.svelte"; + import CardTitle from "$lib/components/ui/card-title.svelte"; + import Input from "$lib/components/ui/input.svelte"; import { api } from "$lib/api/client"; import type { DeviceItem, DeviceRegistrationTokenItem } from "$lib/types/domain"; @@ -112,28 +117,28 @@
{#if error} -

{error}

+

{error}

{/if} {#if loading} - Loading devices… + Loading devices… {:else}
-

Allowed devices

{devices.length}

-

Active devices

{devices.filter((device) => device.is_active).length}

-

Registration tokens

{tokens.length}

+

Allowed devices

{devices.length}

+

Active devices

{devices.filter((device) => device.is_active).length}

+

Registration tokens

{tokens.length}

-
-

Whitelisted devices

-

Imported device/IP allowlist plus new Django-managed entries.

-
-
+ + Whitelisted devices + Imported device/IP allowlist plus new Django-managed entries. + + {#each devices as device} -
+

{device.label || device.ip_address}

@@ -143,65 +148,69 @@

-

{device.is_active ? "Active" : "Inactive"}

- +

{device.is_active ? "Active" : "Inactive"}

+
{/each} -
+
-
-

Registration tokens

-

Manual tokens for controlled device onboarding.

-
-
+ + Registration tokens + Manual tokens for controlled device onboarding. + + {#each tokens as token} -
+

{token.label || "Untitled token"}

{token.token}

Expires {new Date(token.expires_at).toLocaleString()} {token.used_at ? `• used ${new Date(token.used_at).toLocaleString()}` : "• unused"}

- +
{/each} -
+
-
-

Add device

-

Create a whitelist entry directly in the new backend.

-
+ + Add device + Create a whitelist entry directly in the new backend. + +
- - - - - + + + + +
+
-
-

Create registration token

-

Generate a fresh onboarding token with an expiry window.

-
+ + Create registration token + Generate a fresh onboarding token with an expiry window. + +
- - + +
+
diff --git a/frontend/src/routes/app/events/+page.svelte b/frontend/src/routes/app/events/+page.svelte index 268ea50..b75373c 100644 --- a/frontend/src/routes/app/events/+page.svelte +++ b/frontend/src/routes/app/events/+page.svelte @@ -2,6 +2,13 @@ import { onMount } from "svelte"; import Button from "$lib/components/ui/button.svelte"; import Card from "$lib/components/ui/card.svelte"; + import CardContent from "$lib/components/ui/card-content.svelte"; + import CardDescription from "$lib/components/ui/card-description.svelte"; + import CardHeader from "$lib/components/ui/card-header.svelte"; + import CardTitle from "$lib/components/ui/card-title.svelte"; + import Input from "$lib/components/ui/input.svelte"; + import Select from "$lib/components/ui/select.svelte"; + import Textarea from "$lib/components/ui/textarea.svelte"; import { api } from "$lib/api/client"; import type { Business, EventItem } from "$lib/types/domain"; @@ -22,7 +29,7 @@ end_datetime: "", all_day: false, location: "", - color: "#ba6c46", + color: "#09090b", recurrence_type: "none", recurrence_end_date: "" }; @@ -70,7 +77,7 @@ end_datetime: "", all_day: false, location: "", - color: "#ba6c46", + color: "#09090b", recurrence_type: "none", recurrence_end_date: "" }; @@ -94,7 +101,7 @@ end_datetime: event.end_datetime.slice(0, 16), all_day: event.all_day, location: event.location, - color: event.color || "#ba6c46", + color: event.color || "#09090b", recurrence_type: event.recurrence_type, recurrence_end_date: event.recurrence_end_date || "" }; @@ -119,32 +126,33 @@

Event planner

- {#each businesses as business} {/each} - +
{#if error} -

{error}

+

{error}

{/if}
-
-

Upcoming events

-

Imported events plus newly created Django-side events.

-
+ + Upcoming events + Imported events plus newly created Django-side events. + + {#if loading}

Loading events…

{:else}
{#each events as event} -
+

{event.title}

@@ -154,49 +162,51 @@
- -
+ +
{/each}
{/if} + -
-

Create event

-

{editingEventId ? "Update an existing event." : "A lean replacement for the old calendar modal stack."}

-
+ + {editingEventId ? "Edit event" : "Create event"} + {editingEventId ? "Update an existing event." : "A lean replacement for the old calendar modal stack."} + +
- {#each businesses as business} {/each} - - - + + +
- - - - - - + + + + - + +
- +
+
diff --git a/frontend/src/routes/app/inventory/+page.svelte b/frontend/src/routes/app/inventory/+page.svelte index 276b546..6f02c94 100644 --- a/frontend/src/routes/app/inventory/+page.svelte +++ b/frontend/src/routes/app/inventory/+page.svelte @@ -2,6 +2,9 @@ import { onMount } from "svelte"; import Button from "$lib/components/ui/button.svelte"; import Card from "$lib/components/ui/card.svelte"; + import CardContent from "$lib/components/ui/card-content.svelte"; + import Input from "$lib/components/ui/input.svelte"; + import Select from "$lib/components/ui/select.svelte"; import { api } from "$lib/api/client"; import type { Category, InventoryItem } from "$lib/types/domain"; @@ -42,52 +45,52 @@

Inventory health

- - + +
{#if error} -

{error}

+

{error}

{/if}
-

Tracked products

{rows.length}

-

Zero stock

{rows.filter((row) => row.quantity_on_hand <= 0).length}

-

Low stock

{rows.filter((row) => row.quantity_on_hand > 0 && row.quantity_on_hand < 5).length}

+

Tracked products

{rows.length}

+

Zero stock

{rows.filter((row) => row.quantity_on_hand <= 0).length}

+

Low stock

{rows.filter((row) => row.quantity_on_hand > 0 && row.quantity_on_hand < 5).length}

+ {#if loading}

Loading inventory…

{:else} -
- {#each rows as row} -
-
-
-

{row.product_name}

-

{row.gtin || "No GTIN"} • {row.category_names.join(", ") || "Uncategorized"}

-

- VAT {row.vat_rate.toFixed(2)} • Purchase {row.net_purchase_price.toFixed(2)} • Sales {row.display_sales_price.toFixed(2)} -

-
-
-

- {row.quantity_on_hand.toFixed(3)} -

-

{row.uom}

-
+ {#each rows as row} +
+
+
+

{row.product_name}

+

{row.gtin || "No GTIN"} • {row.category_names.join(", ") || "Uncategorized"}

+

+ VAT {row.vat_rate.toFixed(2)} • Purchase {row.net_purchase_price.toFixed(2)} • Sales {row.display_sales_price.toFixed(2)} +

+
+
+

+ {row.quantity_on_hand.toFixed(3)} +

+

{row.uom}

- {/each} -
+
+ {/each} {/if} +
diff --git a/frontend/src/routes/app/invoices/+page.svelte b/frontend/src/routes/app/invoices/+page.svelte index 88f8b0f..c23de53 100644 --- a/frontend/src/routes/app/invoices/+page.svelte +++ b/frontend/src/routes/app/invoices/+page.svelte @@ -2,6 +2,10 @@ import { onMount } from "svelte"; import Card from "$lib/components/ui/card.svelte"; import Button from "$lib/components/ui/button.svelte"; + import Input from "$lib/components/ui/input.svelte"; + import Label from "$lib/components/ui/label.svelte"; + import Select from "$lib/components/ui/select.svelte"; + import Textarea from "$lib/components/ui/textarea.svelte"; import { api } from "$lib/api/client"; import type { Business, Category, Invoice, Product, Vendor } from "$lib/types/domain"; @@ -173,17 +177,12 @@

Invoice tracker

- loadData(query)} - placeholder="Search vendor, note, or invoice number" - /> + loadData(query)} placeholder="Search vendor, note, or invoice number" />
{#if error} -

{error}

+

{error}

{/if}
@@ -203,7 +202,7 @@
{#each invoices as invoice}
{#each form.line_items as line, index}
- {#each products as product} {/each} - - - + + +
{(Number(line.quantity || 0) * Number(line.unit_price || 0)).toFixed(2)}
@@ -363,11 +362,11 @@
- - + +
-
+

Draft subtotal

{subtotal.toFixed(2)} {form.currency}

diff --git a/frontend/src/routes/app/schedule/+page.svelte b/frontend/src/routes/app/schedule/+page.svelte index 21bf939..b8a2755 100644 --- a/frontend/src/routes/app/schedule/+page.svelte +++ b/frontend/src/routes/app/schedule/+page.svelte @@ -2,6 +2,11 @@ import { onMount } from "svelte"; import Button from "$lib/components/ui/button.svelte"; import Card from "$lib/components/ui/card.svelte"; + import CardContent from "$lib/components/ui/card-content.svelte"; + import CardDescription from "$lib/components/ui/card-description.svelte"; + import CardHeader from "$lib/components/ui/card-header.svelte"; + import CardTitle from "$lib/components/ui/card-title.svelte"; + import Select from "$lib/components/ui/select.svelte"; import { api } from "$lib/api/client"; import type { Business, ScheduleOverview } from "$lib/types/domain"; @@ -38,54 +43,54 @@

Schedule overview

- {#each businesses as business} {/each} - +
{#if error} -

{error}

+

{error}

{:else if loading || !overview} - Loading schedule… + Loading schedule… {:else}
-

Roles

{overview.roles.length}

-

Templates

{overview.templates.length}

-

Assignments

{overview.assignments.length}

+

Roles

{overview.roles.length}

+

Templates

{overview.templates.length}

+

Assignments

{overview.assignments.length}

-
-

Roles

-

Shift role catalogue by business.

-
-
+ + Roles + Shift role catalogue by business. + + {#each overview.roles as role}

{role.name}

{role.business_name}

-
+
{/each} -
+
-
-

Upcoming templates

-

Recurring shift definitions and staffing targets.

-
-
+ + Upcoming templates + Recurring shift definitions and staffing targets. + + {#each overview.templates as template} -
+

{template.name}

@@ -102,15 +107,15 @@
{/each} -
+ -
-

Assignments

-

Upcoming user allocations against templates.

-
-
+ + Assignments + Upcoming user allocations against templates. + + {#each overview.assignments as assignment}
@@ -124,7 +129,7 @@
{/each} -
+
{/if} diff --git a/frontend/src/routes/app/settings/+page.svelte b/frontend/src/routes/app/settings/+page.svelte index 2794c50..f0560eb 100644 --- a/frontend/src/routes/app/settings/+page.svelte +++ b/frontend/src/routes/app/settings/+page.svelte @@ -2,6 +2,8 @@ import { onMount } from "svelte"; import Button from "$lib/components/ui/button.svelte"; import Card from "$lib/components/ui/card.svelte"; + import Input from "$lib/components/ui/input.svelte"; + import Select from "$lib/components/ui/select.svelte"; import { api } from "$lib/api/client"; import type { Business, SettingsOverview, SettingsUser } from "$lib/types/domain"; @@ -132,7 +134,7 @@
{#if error} -

{error}

+

{error}

{/if} {#if loading || !overview} @@ -153,7 +155,7 @@
{#each overview.users as user} - {/each} @@ -176,7 +178,7 @@
{#each overview.roles as role} -
+

{role.name}

@@ -208,17 +210,17 @@
- - - - + + + +
- {#each overview.roles as role} {/each} - +

Business access

@@ -226,7 +228,7 @@ {#each businesses as business}
- +
{#if error} -

{error}

+

{error}

{/if}
-
- {#each businesses as business} {/each} - - + +
{#if loading} @@ -147,7 +155,7 @@ {:else}
{#each vendors as vendor} - {/each}
{/if} +
-
+
-

{editingVendorId ? "Edit vendor" : "Create vendor"}

-

Manage linkage to businesses and invoice categories.

+ {editingVendorId ? "Edit vendor" : "Create vendor"} + Manage linkage to businesses and invoice categories.
{#if editingVendorId} {/if} -
+ +
- - - - - + + + + +
- - + +

Businesses

@@ -192,7 +202,7 @@ {#each businesses as business} +
diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte index 045f368..1b6386b 100644 --- a/frontend/src/routes/login/+page.svelte +++ b/frontend/src/routes/login/+page.svelte @@ -3,6 +3,12 @@ import { goto } from "$app/navigation"; import Button from "$lib/components/ui/button.svelte"; import Card from "$lib/components/ui/card.svelte"; + import CardContent from "$lib/components/ui/card-content.svelte"; + import CardDescription from "$lib/components/ui/card-description.svelte"; + import CardHeader from "$lib/components/ui/card-header.svelte"; + import CardTitle from "$lib/components/ui/card-title.svelte"; + import Input from "$lib/components/ui/input.svelte"; + import Label from "$lib/components/ui/label.svelte"; import { api } from "$lib/api/client"; import { authUser, authReady, bootstrapAuth } from "$lib/stores/auth"; @@ -43,41 +49,70 @@ {#if checkingSession}
-
+

Session

Checking authentication…

-
+
{:else} -
-
-
-

Django + Svelte port

-

- Replace the brittle FastAPI tangle with a session-based operations platform. -

-

- The new frontend is organized around stable domains, smaller route modules, and a backend that uses Django ORM and admin instead of runtime schema patching. -

+
+
+
+
+

Django + Svelte port

+

+ Hospitality operations, rebuilt as a clean black-and-white control surface. +

+

+ Session auth, Django ORM, scoped business access, and domain-focused screens without the legacy FastAPI sprawl. +

+
+ +
+ + +

Backend

+

Django

+
+
+ + +

Frontend

+

SvelteKit

+
+
+ + +

UI

+

Vanilla shadcn style

+
+
+
- -
-
- - -
-
- - -
- {#if error} -

{error}

- {/if} - -
+ + + Sign in + Use the Django session auth flow for the new operations app. + + +
+
+ + +
+
+ + +
+ {#if error} +

{error}

+ {/if} + +
+