Commit graph

16 commits

Author SHA1 Message Date
Claude
8d1c2e4eb5
feat: hamburger menu for mobile nav
On screens ≤640px the tab row collapses; a hamburger button (Material
Symbols menu/close icon) appears in the nav-right area. Tapping it
opens a fixed full-width dropdown below the header with full-height
tap targets and a left accent border on the active item. Tapping a tab
or anywhere outside closes the menu. Desktop layout is unchanged —
nav-tabs uses display:contents so the buttons remain direct flex
children of nav.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-31 03:30:45 +00:00
Claude
1f8fe21eaf
feat: two-step bar charge flow with full-screen PIN overlay
Staff enter amount and note, click Charge; a full-screen patron-facing
screen appears showing the charge total, member name, and a large PIN
input that auto-focuses. PIN errors stay on the overlay; Cancel returns
to the amount form so staff can adjust before handing the device back.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 17:38:18 +00:00
Claude
7b4e33254c
feat: admin-configurable default paper size for receipts/statements
Adds a Paper Size setting (A4/A5) to the General section of Admin
settings. Receipts and statements pre-select the configured size and
apply the correct @page margins; staff can still override per-print.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 17:29:59 +00:00
Claude
b1fcc3dbe9
Add configurable display timezone; default to server's local timezone
- _server_timezone(): detects IANA timezone from /etc/timezone or
  /etc/localtime symlink at startup; used as the CONFIG default
- CONFIG: new "timezone" key set to server's detected timezone
- AppSettingsUpdate: new optional timezone field
- _display_tz(), _fmt_dt(), _now_display() helpers: convert stored UTC
  datetimes to the configured timezone for display; falls back to server
  local if the setting is empty or the zone name is invalid
- receipt(): transaction timestamp uses _fmt_dt() instead of raw UTC slice
- statement(): row timestamps and "Generated" line use _fmt_dt()/_now_display()
- Admin settings: Timezone text input (IANA name) in General section
- app.js: loadAdminSettings/saveSettings handle timezone field

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 16:59:54 +00:00
Claude
09df5efb07
Redesign receipts/statement to match spec; add logo upload
Receipts:
- Font size raised to 11pt base (labels 9pt, amounts 13pt bold)
- Each field now shows LABEL (small, uppercase, gray) above VALUE —
  two cells per row in a 1fr/1fr CSS grid, matching the provided samples
- Business header: left column = address lines, right column = Tel/Email/Web
- Charge receipt: STAFF+TRANSACTION / CHARGE+TIME / AMOUNT+BALANCE
- Top-up/Withdrawal receipt: STAFF+TXN / TRANSFER_TYPE+TIME /
  AMOUNT+BALANCE / TRANSFER_TYPE+TRANSFER_REF
- Print button moved into the paper-size controls bar

Statement:
- Reduced from 9 to 7 columns: Date, Reference, Type, Venue, Staff,
  Amount (+/-), Balance — removes the separate Charge/Credit split
- Amount shown as "+ £X.XX" (green) or "- £X.XX" (red)
- Sub-row shows "Transfer type: X — Ref" for top-ups/withdrawals,
  or the note text for charges

Logo:
- New POST /admin/logo endpoint: accepts image upload, saves to
  static/logo.{ext}, auto-updates logo_url setting
- New logo_max_width / logo_max_height config fields (default 200×80px)
- Admin branding section: file upload input + max-width/height fields
- python-multipart added to requirements.txt (needed for file upload)

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 16:55:23 +00:00
Claude
79ae833fa9
Replace literal em-dashes with — HTML entities
Fixes garbled display ("â€"") on Windows where the UTF-8 bytes for U+2014
were being misread as Windows-1252. All em-dash occurrences in index.html,
app.js, and common.js are now expressed as HTML entities.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 16:42:46 +00:00
Claude
8616ff1d49
Add business branding, transaction references, and redesigned receipts
- CONFIG: add business address/contact, logo URL+alignment, venue names,
  transaction ref prefix, transfer types list, and 14 localizable receipt
  labels plus per-receipt-type footer fields
- DB: add transfer_type and transfer_ref columns to ledger_entries
  (init_db + migrate_db); fix duplicate return in pos_user
- Pydantic: extend AppSettingsUpdate with all new settings;
  add transfer_type/transfer_ref to TopupRequest and WithdrawalRequest
- /topup and /withdrawal: persist transfer_type and transfer_ref
- /config: return transfer_types as parsed array for frontend dropdowns
- New helpers: _txn_ref(), _logo_html(), _biz_header_html()
- New RECEIPT_CSS with 2-column grid layout; receipt() fully redesigned
  with business header, auto-generated TXN reference, separate charge vs
  top-up/withdrawal layouts; statement() adds Reference column and
  transfer-detail sub-rows
- index.html: Transfer Type + Transfer Reference fields on cashier/
  withdrawal panels; admin settings expanded into organized sections
  (General, Business Address, Branding, Transactions, Receipt Labels,
  Receipt Footers)
- app.js: populateTransferTypes() called on startup and after settings
  save; doTopup/doWithdrawal send transfer fields; clearCashierSelection
  clears new fields; loadAdminSettings/saveSettings handle all new fields

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 15:44:27 +00:00
Claude
21b6791f4c
Move overdraft override to per-member setting on member record
Instead of a per-transaction checkbox at point of sale, the overdraft
override is now a persistent flag on each member, set via the Edit Member
modal by the appropriate role.

Schema:
- members.overdraft_override INTEGER DEFAULT NULL
  (NULL = follow global policy, 1 = explicitly allowed, 0 = explicitly blocked)
- migrate_db(): ALTER TABLE members ADD COLUMN ... for existing databases

Charge logic (combining global policy + member flag):
- never:          always block (member flag ignored)
- always:         always allow (member flag ignored)
- staff_override: allow only if member flag = 1
- admin_override: allow only if member flag = 1 (only admin can set it via UI)
- staff_block:    allow unless member flag = 0 (explicitly blocked)

Edit Member modal:
- Shows "Allow overdraft for this member" for staff_override / admin_override
  (admin_override hidden from non-admins)
- Shows "Block overdraft for this member" for staff_block
- Hidden for never / always policies
- Checkbox state pre-populated from current member.overdraft_override value

Removed the per-transaction barOverrideRow that was added in the previous
commit — it has been superseded by this per-member approach.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 15:08:19 +00:00
Claude
7fa74963b0
Replace overdraft checkbox with 5-option policy dropdown
Policies:
- never            – not allowed (default)
- always           – allowed for all charges
- staff_override   – default no; staff sees checkbox to allow per charge
- admin_override   – default no; only admins see the allow-per-charge checkbox
- staff_block      – default yes; staff sees checkbox to block per charge

Backend:
- CONFIG: allow_negative_balance → overdraft_policy: "never"
- migrate_db(): converts old allow_negative_balance setting in app_settings
  to the equivalent overdraft_policy value on first startup after upgrade
- ChargeRequest: new optional overdraft_override: bool = False
- POST /charge: full policy logic; admin_override enforced server-side
  so a non-admin can't bypass it by sending override=true
- POST /admin/settings: validates policy value before saving

Frontend:
- Admin settings: checkbox replaced by <select> with five options
- Bar form: barOverrideRow (hidden by default); selectBarMember() shows
  it with the right label when policy is staff_override / admin_override
  (admin only) / staff_block; hidden for never and always
- clearBarSelection() resets the override checkbox and hides the row

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 14:39:43 +00:00
Claude
acd8ff3fd0
Add withdrawal feature to cashier tab
Cashiers can process credit withdrawals (cash-back) for members:
- Requires member PIN to authorize
- Always checks sufficient balance (overdraft not allowed for withdrawals)
- Appears as 'withdrawal' type in ledger, statement, and on receipt
- Receipt opens automatically, same as top-up/charge

Backend:
- migrate_db() now also recreates ledger_entries with type constraint
  extended to include 'withdrawal' (existing rows preserved)
- POST /withdrawal endpoint (cashier_user required)
- Receipt label map updated to include Withdrawal

Frontend:
- Cashier tab now shows two panels side-by-side: Top Up and Withdrawal
- Each panel has its own message area so they don't overwrite each other
- Withdrawal panel includes PIN field; top-up panel unchanged

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 14:31:16 +00:00
Claude
68a35e5bff
Split staff role into cashier/pos-staff; fix admin tab visibility
Roles:
- cashier: Members + Cashier (top-up) tabs; POST /topup enforced at API level
- pos-staff: Members + Bar (charge) tabs; POST /charge enforced at API level
- admin: all tabs including Admin panel

Changes:
- migrate_db() recreates staff_accounts with new CHECK constraint and
  converts any existing 'staff' rows to 'pos-staff' on startup
- cashier_user / pos_user FastAPI dependencies added; applied to /topup and /charge
- Role dropdowns in admin panel updated to the three new values
- startApp() hides irrelevant tabs per role on login
- doLogout() resets all tab visibility so the next login starts clean
- fmtRole() formats role names ('pos-staff' → 'POS Staff') in the accounts table

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 14:19:41 +00:00
Claude
803d157d25
Fix login overlay visible on reopen when session is still active
The login overlay lacked the `hidden` class by default, so on every page
load it was visible and blocking the app. On fresh load with a valid session,
boot() skips showLogin() (no submit handler added), leaving the overlay
covering the screen with a non-functional form. Adding `hidden` to the HTML
and explicitly hiding the overlay in startApp() fixes both paths.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 13:42:41 +00:00
Claude
a5c9af1ca6
Add staff auth, admin area, currency decimal input
Auth system
- staff_accounts table: name, username, bcrypt password, role (staff|admin)
- Session tokens in memory (8-hour TTL), httpOnly cookie
- POST /auth/login, /auth/logout, GET /auth/me
- All API endpoints now require a valid session
- Default admin account seeded on first run (admin/admin), printed to console
- Staff name for transactions comes from the session, no more dropdown

Currency input fix
- Amount inputs are now decimal (step=0.01); users enter 1.00 not 100
- Frontend multiplies by cfg.currency_divisor before POSTing
- TopupRequest/ChargeRequest no longer include staff_name (from session)

Admin area (4th tab, admin role only)
- App Settings: club name, currency symbol, major/minor unit names,
  divisor, min/max topup, max charge, receipt footer, allow overdraft
- Settings persisted in app_settings DB table; merged with CONFIG defaults
  at startup and refreshed after each save
- Staff Accounts: list with edit modal (name, username, password, role,
  active flag) and delete; Add Account inline form
- /admin/settings GET/POST, /admin/staff-accounts CRUD
- /config endpoint exposes live settings to frontend on every page load

receipt_footer field rendered on both receipt and statement print views

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 09:19:07 +00:00
Claude
79d51973cd
Remove product search from bar tab
Removed product search field, results list, barProductLookup(),
and selectProduct() from the bar view in index.html, bar.html,
app.js, and bar.js. Backend /products endpoints are unchanged.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 08:52:18 +00:00
Claude
6c155d00bb
Restore three-view SPA; add member edit and delete
- / now serves index.html (three-view SPA: Members, Cashier, Bar)
- /cashier and /bar remain as standalone pages (unchanged)
- Members view: Edit button on every row opens a modal to update
  name, member number, and optionally PIN. Delete button only appears
  when balance is exactly 0; confirmation dialog before deletion removes
  the member and their ledger entries.
- PUT /members/{id}: updates any combination of name/member_number/pin;
  guards against duplicate member numbers.
- DELETE /members/{id}: rejects with 400 if balance != 0, otherwise
  deletes ledger entries then member row.
- Modal styles added to style.css; app.js rebuilt as combined SPA script
  (loads common.js for shared helpers).

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 08:33:44 +00:00
Claude
fa4884bdb4
Add store-credit web app (FastAPI + SQLite)
- main.py: single-file backend with Member, LedgerEntry, Product models;
  endpoints for register, list/search members, topup (cashier), charge (bar,
  PIN-verified), transaction history, printable HTML statement, product CRUD.
  All monetary values stored as integers (pence). bcrypt PIN hashing.
  Admin-tunable CONFIG dict at top of file.
- static/index.html + style.css + app.js: three-view SPA (Members, Cashier,
  Bar) with member search, product search with member-price support, and
  XSS-safe rendering throughout.
- requirements.txt: fastapi, uvicorn, bcrypt only.

https://claude.ai/code/session_01JuRTR5Xjx8emQsyerBgGU7
2026-05-30 05:21:01 +00:00