Commit Graph

37 Commits

Author SHA1 Message Date
type-two
b46930d468 feat(staff): time clock — clock on/off = shift = timecard
staff_shift table (one open shift per staff via partial unique index). /admin/clock
(status+today total), /clock/in (idempotent), /clock/out; /admin/timecards (admin)
= per-staff hours + recent shifts over a window. search.html top bar gets a live
clock widget (🟢 On 2h14m · Clock off · Log out; Clock on when off) ticking every 30s;
Log out warns if still clocked on. admin Staff tab gains a Timecards report.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:40:02 +10:00
type-two
5e2a9e7ab1 feat(auth): staff accounts + roles — operators run the store, admins keep the keys
Token resolver now checks the env owner-token (admin) then a staff table (name/token/
role). New require_admin gates /settings/* (API keys/connections) + staff CRUD; everything
operational (search/scan/inventory/labels/intake) stays on require_token so staff can do it.
admin.html: /admin/me drives ROLE; data-admin nav (Staff + Connections) hidden for staff +
go() blocks the views; new Staff tab to add operators, set role, reset/show token once,
deactivate. Tokens shown once on create.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:33:19 +10:00
type-two
13e5200b8d feat(nav): colour crates by type in the rack diagram (material_color)
/nav/rack returns each crate's virtual_crate_type.material_color + type name;
drawRack fills/strokes/arrows each crate in its real colour (Blue Crate #0000FF,
Wooden Rack Bin #BA906A, White Tub #FFFFFF, Black Tub #191970, Shelf #C0C0C0…)
instead of all-blue. Legend lists the crate types present on the rack.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:26:34 +10:00
type-two
cba339151f feat(nav): Crate Info panel — edit label_text + rotate crate.direction in /search
Per-crate panel under Crate Contents: rename (label_text) + a Facing segment
(↑back/↓front/←left/→right) that rotates the crate live — the map arrow flips on
click. CrateEditIn gained 'direction'. Restores the crate editing my tabbed rewrite
dropped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 10:51:41 +10:00
type-two
c4b544373d feat(nav): unified tabbed Search & Inventory workstation (WowPlatter parity)
One persistent central store/rack map with a top tab strip (Scanner / Returns /
Reorganize / Collections / Stock Finder) all driving the shared map, + Store/Rack/
Crate/Item view buttons and an omni scan bar (release/SKU/c12/r3). Reorganize: click
crates IN ORDER (numbered pick badges feed the A–Z distribution planner) + drag racks
to move them on the store map (persists pos_x/pos_z; RackEditIn gained position fields).
Collections edit highlights picked crates on the map. Returns = stub (manual path via
Scanner + Release Info for now).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 09:58:18 +10:00
type-two
48f8579bff feat(shop): P4 wantlist — public 'request a record' page + admin queue
Storefront /wantlist form (themed, email-keyed, guest-friendly) posts to
/shop/wantlist → wantlist table (lazy DDL). Release pages deep-link to it
(prefilled; 'not in stock — request this record' when no copies). Admin gets
a Wantlist tab: pending/found/fulfilled queue with one-click status actions.
Default storefront menu → Records + Wanted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 01:29:00 +10:00
type-two
28a46615a5 feat(shop): entity browse pages — artist/label/genre/style storefront views
records.html reads /artist|label|genre|style/{val} from the path, sets the
filter + an entity header (artist/label name via /shop/{kind}/{id}). /shop/browse
gains artist/label EXISTS filters + primary artist_id; release page links
artist→/artist/{id} and genre/style pills→/{kind}/{name}.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 01:18:15 +10:00
type-two
fcc3d7b2ff feat(storefront): P0 — public Browse (faceted release catalog) + Release detail pages + /shop/{browse,facets,release} API; Woo-transacted
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 00:37:04 +10:00
type-two
0269decb87 feat(search): editable Rack Info (name) + Reorganize planner UI (scan source crates → filter → A-Z distribute into target crates → review → apply)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:58:10 +10:00
type-two
83466389af feat(scanner): scan-to-slot assigner (replace/append/prepend/insert + Test preview + recover-last) + editable crate info (name/label); hdr Content-Type fix
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:46:14 +10:00
type-two
19c6e03c70 feat(search): clickable crate contents → Release Info panel (cover/label/format/country/year/genre/style + all copies + locate); crate genre/style + last-scanned + compress
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:40:06 +10:00
type-two
d6e75667bf feat(customers): CRM — list with spend/orders/last + record page (overview stats, editable info, mailing opt-in, sales history)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 23:17:11 +10:00
type-two
a817b9d163 feat(import): bulk stock-in — resolve Release ID/barcode/catno against disc_release → create inventory (UI + endpoint)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:48:43 +10:00
type-two
4ece321b76 feat(inventory): CRUD backend (add/edit/delete/bulk) — first cream from the WowPlatter audit; + migration audit doc
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:39:17 +10:00
type-two
0b679ba079 fix(images): /img/r serves local copy else 302 to discogs webp (server-side fetch is CF-blocked); drop unused pillow
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:48:08 +10:00
type-two
ddaa6ecedf feat(images): RecordGod WebP cover cache (/img/r/{release_id}) — same-VPS, configurable DISC_IMAGE_DIR, no DealGod dep
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:45:31 +10:00
type-two
af3a611667 feat(collections): interactive editor — click-map crate select, genre/style/price/year rules, live match stats, priority
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:33:15 +10:00
type-two
10af3464ac feat(disc): mirror WowPlatter Discogs metadata into recordgod (disc_sync.py) + locator matches on collection genre/style ids
- disc_sync.py: MariaDB clone -> recordgod disc_* (30.8k releases, genre/style/label/format/identifier/artist/track/master), keeping WowPlatter's own genre/style ids
- locator reads disc_release.genre_ids/style_ids + parses collection 'ID,0' id format
- images stay with DealGod; masters sparse (backfill from Ultra later)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 21:19:47 +10:00
type-two
a1b4538954 fix(collections): is_active is int not bool — cast in queries
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:58:50 +10:00
type-two
879a58992f feat(collections): Collections CRUD + fallback locator + Reorganize scan/apply/compress API + Collections UI
- collections_routes.py: /nav/collections CRUD over virtual_collection + /nav/locate (exact-then-rules)
- navigator_routes.py: /nav/reorg/{scan,apply,compress} (atomic re-file)
- disc_cache gets year/genre_ids/style_ids cols for rule matching (backfill TODO)
- /search: collections list + locator fallback shows the should-be-here zone for unfiled hits

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:56:34 +10:00
type-two
fb533a7701 fix(nav): store view shows functional racks only (rack_purpose<>prop), truncate labels
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:38:29 +10:00
type-two
53b1a64a48 feat(nav): light/dark switch every page + store→rack→crate navigator
- nav.js owns a light/dark toggle (persisted, default light) across all owner pages
- /nav API: spaces, store-layout, rack/{id}, crate/{id} over migrated virtual_* geometry
- /search rebuilt as Store→Rack→Crate drill-down (true rack rotation, per-level slot grid,
  crate facing arrows + BACK/FRONT/L/R, search→locate)
- facing convention (directionYaw, slot-grid, rotatedArrows, world composition) in RECORDGOD_NAVIGATOR_PLAN.md
- no-cache HTML routes so deploys show instantly

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 19:33:24 +10:00
type-two
eb502efaf4 feat(pos): full Sales console + migrate customers/discounts/settings 2026-06-22 02:40:35 +10:00
type-two
584fd140ad feat: Search/locate workstation + Search & Sales in nav (cache-bust nav.js) 2026-06-22 01:57:54 +10:00
type-two
80ff669cac feat(pos): Sales tab — ring-up, discounts, payment plans, holds, locate 2026-06-22 01:43:14 +10:00
type-two
19e3cb131f feat: clean stub URLs (/builder /shop /admin /dash) + store geometry + storefront 2026-06-21 23:16:57 +10:00
type-two
a9076af19d feat(builder): storefront customizer 2026-06-21 21:29:55 +10:00
type-two
8c25b65d7e feat(virtual): finish the 3D store port — per-room scoping, decals, portals
Scene was dumping all 3 rooms' racks/crates/lights into one room, and the
served decals + portals were never rendered (trapped in booth-room).

- /virtual/scene scopes to ONE room (?space=, default = is_default space);
  filters racks/crates/lights/cameras/portals/decals/covers by space_id
- render decals (image + canvas-text planes) and portal doorways; walk into
  a portal -> loads the linked room
- portal links_to_id is the PAIRED portal's id, not a space — resolve through
  it to the destination room (to_space)
- copy the 29 referenced decal webp/svg into webstore/assets (self-contained,
  served /store/assets); rewrite WP /wp-content/uploads/ paths in the scene API
- skip the 17MB logo .glb mesh decal (flat logo-blue.webp covers branding)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 21:00:02 +10:00
type-two
b4cdc8aee4 feat(admin): Store Map + Intake/Crates/Reports 2026-06-21 20:03:08 +10:00
3732dfa325 feat(admin): Intake, Crates, Reports views + endpoints 2026-06-21 19:50:47 +10:00
1f388f241c feat(admin): MEGA admin dash + /admin endpoints (stats/inventory/orders) 2026-06-21 19:19:56 +10:00
0e8c84b0e7 feat(dash): connection testing — Discogs / Woo / DealGod
POST /settings/test/{service} validates stored creds against the live service
(Discogs identity, Woo orders, DealGod /api/me) so "connected" means connected.
Dash gets per-service Test buttons with green/amber status.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 15:03:18 +10:00
5c10bdc06d feat: self-contained enrichment — disc_cache (own catalog copy), no external DB dependency
- schema: disc_cache (release_id→title/artist/thumb/weight); build_disc_cache.py populates it
  from the full Discogs dump on ultra (30,694 stocked releases).
- app/mirror.py: enrich() now reads RecordGod's OWN disc_cache via the main DB — no live
  discogs_full / dealgod-DB dependency. DealGod reached only via API key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:16:56 +10:00
9b8ecdb84d feat: deploy-ready — env-aware Discogs enrichment + Dockerfile
- app/mirror.py: centralised enrichment, DISCOGS_MIRROR_KIND selects full (ultra `release`)
  vs vps (`discogs_release`); virtual.py + wowplatter_v1.py use it.
- Dockerfile + .dockerignore for the VPS container.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 13:08:49 +10:00
e1fcf94f93 feat: standalone RecordGod web UI — landing, control-room dash, encrypted secrets vault
- app/vault.py: Fernet-encrypted credential store (app_secret table); key lives only in
  RECORDGOD_SECRET_KEY (.env, gitignored) — refuses to store anything if the key is unset.
- app/settings_routes.py: admin-gated GET/POST /settings/secrets; values encrypted at rest,
  NEVER returned. Known fields: Woo / Discogs / Cloudflare / DealGod credentials.
- site/index.html: recordgod.com landing. site/dash.html: control room (token gate +
  paste-your-credentials form). main.py loads .env, mounts site at /, store at /store.
- Proven: 401 on bad token; DB holds ciphertext (gAAAA…), not plaintext.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 12:40:07 +10:00
8b48010b50 feat(virtual): lift the 3D store out of WordPress — scene API + raw Three.js renderer
- migrate_virtual.py: all 15 wp_rmp_disc_virtual_* tables → Postgres (~700 rows).
- app/virtual.py (public): GET /virtual/scene (geometry + slim front covers enriched
  from discogs_full), /virtual/crate/{id}/records (digging), /virtual/locate (locate-stock).
  Replaces payload.php — no WordPress bootstrap.
- webstore/index.html: walkable raw Three.js store (room/racks/crates from the data,
  pointer-lock FPS, click-to-dig hook), served same-origin at /store.
- Proven: 51 racks, 346 crates, 277 covers; locate release→crate works.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 17:59:52 +10:00
d75f27a691 feat: RecordGod founding — service scaffold + MariaDB→Postgres migration
- FastAPI twin of DealGod (async SQLAlchemy/asyncpg). wowplatter/v1: ping,
  inventory/lookup, inventory/intake (enriches from discogs_full by release_id).
- schema v2: unified inventory (vinyl+general), slim crate, sales/sale_items.
  Discogs catalog NOT copied — joined from discogs_full at read time.
- migrate.py: 34,543 shop rows moved (vs ~3.2M in the WowPlatter clone).
- Smoke test green; plan/readme carry the architecture + UI boundary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 17:18:50 +10:00