Commit Graph

87 Commits

Author SHA1 Message Date
ryan
3b0daeae0c feat: lessons station + technician workbench + polish
All checks were successful
CI / ci (pull_request) Successful in 26s
CI / e2e (pull_request) Successful in 1m8s
Phase 3: Technician workbench
- Focused single-ticket view with Work, Parts, Photos, Notes sections
- Template quick-add for line items, inline add/delete
- Ticket selector for multiple assigned tickets

Phase 4: Lessons desk view
- Today overview: all instructors' sessions, group by time/instructor
- Quick check-in (attended/missed/cancelled) buttons
- Highlights upcoming and overdue sessions
- Schedule view: weekly grid with instructor filter, open slots

Phase 5: Lessons instructor view
- My Sessions (today) + Week calendar sub-views
- Session detail with attendance, notes, plan items
- Week calendar with session blocks, click to open detail dialog

Phase 6: Polish
- Permission-based routing: desk vs tech/instructor views
- Build and lint clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:46:51 +00:00
ryan
0411df57eb feat: repairs station — technician workbench
- Focused single-ticket workbench with sections: Work, Parts, Photos, Notes
- Template quick-add for line items
- Auto-filters to assigned tickets for the logged-in technician
- Ticket selector when multiple assigned
- Permission routing: repairs.edit → desk view, view-only → tech workbench

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:37:01 +00:00
ryan
082e388799 feat: unified station mode with POS + repairs desk view
Phase 1: Station shell
- /station route replaces /pos (with redirect)
- Shared lock screen, activity tracking, auto-lock timer
- Permission-gated tab bar (POS | Repairs | Lessons)
- POSRegister embedded mode (skip lock/timer/topbar)
- Sidebar navigates to /station

Phase 2: Repairs station — front desk
- Status bar with count badges per status group, clickable filters
- Ticket queue panel with search and status filtering
- Ticket detail panel with status progress, notes, photos, line items
- Full stepped intake form: customer → item → problem/estimate → review
- Service template quick-add for line items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 01:33:06 +00:00
ryan
45fd6d34eb feat: email receipts and repair estimates
All checks were successful
CI / ci (pull_request) Successful in 24s
CI / e2e (pull_request) Successful in 1m2s
Backend:
- Server-side HTML email templates (receipt + estimate) with inline CSS
- POST /v1/transactions/:id/email-receipt with per-transaction rate limiting
- POST /v1/repair-tickets/:id/email-estimate with per-ticket rate limiting
- customerEmail field added to receipt and ticket detail responses
- Test email provider for API tests (logs instead of sending)

Frontend:
- POS payment dialog Email button enabled with inline email input
- Pre-fills customer email from linked account
- Repair ticket detail page has Email Estimate button with dialog
- Pre-fills from account email

Tests:
- 12 unit tests for email template renderers
- 8 API tests for email receipt/estimate endpoints and validation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:32:52 +00:00
ryan
924a28e201 feat: forced PIN setup modal on all authenticated pages
All checks were successful
CI / ci (pull_request) Successful in 29s
CI / e2e (pull_request) Successful in 1m7s
Replaces the alert banner with a blocking modal dialog that requires
users to set a PIN before they can use the app. Cannot be dismissed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:01:14 +00:00
ryan
2cd646ddea fix: remove unused Link import from profile page
All checks were successful
CI / ci (pull_request) Successful in 27s
CI / e2e (pull_request) Successful in 1m6s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:57:31 +00:00
ryan
3f9e125412 fix: move PIN warning banner to authenticated layout for all pages
Some checks failed
CI / ci (pull_request) Failing after 26s
CI / e2e (pull_request) Has been skipped
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:51:39 +00:00
ryan
96d2a966d7 feat: tabbed profile page with PIN setup and auto employee numbers
All checks were successful
CI / ci (pull_request) Successful in 25s
CI / e2e (pull_request) Successful in 55s
- Profile page split into Account, Security, Appearance tabs
- Security tab: change password + set/change/remove POS PIN
- Warning banner with link to Security tab when no PIN is set
- /auth/me returns employeeNumber and hasPin
- Migration 0046: Postgres trigger auto-assigns sequential employee
  numbers starting at 1001, backfills existing users
- Added shadcn Alert component

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 19:41:36 +00:00
ryan
613784a1cc feat: set browser tab title and favicon from customer branding
All checks were successful
CI / ci (pull_request) Successful in 27s
CI / e2e (pull_request) Successful in 1m15s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:15:32 +00:00
ryan
bc8613bbbc feat: password reset flow with welcome emails
All checks were successful
CI / ci (pull_request) Successful in 27s
CI / e2e (pull_request) Successful in 1m0s
- POST /auth/forgot-password with welcome/reset email templates
- POST /auth/reset-password with Zod validation, 4-hour tokens
- Per-email rate limiting (3/hr) via Valkey, no user enumeration
- Login page "Forgot password?" toggle with inline form
- /reset-password page for setting new password from email link
- Initial user seed sends welcome email instead of requiring password
- CLI script for force-resetting passwords via kubectl exec
- APP_URL env var in chart, removed INITIAL_USER_PASSWORD

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 17:09:23 +00:00
ryan
99fdaaa05a fix: regenerate route tree to include reports/daily route
All checks were successful
CI / ci (pull_request) Successful in 21s
CI / e2e (pull_request) Successful in 55s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:22:20 +00:00
ryan
e589ff02f0 fix: remove unused imports to pass lint
All checks were successful
CI / ci (pull_request) Successful in 22s
CI / e2e (pull_request) Successful in 59s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:17:42 +00:00
ryan
da4b765b14 feat: show customer branding on login page
Some checks failed
CI / ci (pull_request) Failing after 20s
CI / e2e (pull_request) Has been skipped
Adds public /v1/store/branding and /v1/store/logo endpoints so the
login page can display the customer's name and logo without auth,
with "Powered by LunarFront" underneath — matching the sidebar style.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:14:07 +00:00
ryan
326b30161b fix: correct _transaction prop name to transaction in POSItemPanel
Some checks failed
Build & Release / build (push) Failing after 35s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:10:15 +00:00
ryan
7d9aeaf188 feat: named registers, X/Z reports, daily rollup, fix drawerSessionId
Registers:
- New register table with location association
- CRUD service + API routes (POST/GET/PATCH/DELETE /registers)
- Drawer sessions now link to a register via registerId
- Register ID persisted in localStorage per device

X/Z Reports:
- ReportService with getDrawerReport() (X or Z depending on session state)
- Z report auto-displayed on drawer close in the drawer dialog
- X report (Current Shift Report) button on open drawer view
- Report shows: sales summary, payment breakdown, discounts, cash accountability, adjustments

Daily Rollup:
- ReportService.getDailyReport() aggregates all sessions at a location for a date
- New /reports/daily endpoint with locationId + date params
- Frontend daily report page with date picker, location selector, session breakdown

Critical Fix:
- drawerSessionId is now populated on transactions when completing (was never set before)
- This enables accurate per-drawer reporting and cash accountability

Migration 0044: register table, drawer_session.register_id column

Tests: 14 new (register CRUD, drawer report X/Z, drawerSessionId population, daily rollup, register-drawer link)
Full suite: 367 passed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
be8cc0ad8b fix: code review fixes + unit/API tests for repair-POS integration
Code review fixes:
- Wrap createFromRepairTicket() in DB transaction for atomicity
- Wrap complete() inventory + status updates in DB transaction
- Repair ticket status update now atomic with transaction completion
- Add Zod validation on from-repair route body
- Fix requiresDiscountOverride: threshold and manual_discount are independent checks
- Order discount distributes proportionally across line items (not first-only)
- Extract shared receipt calculations into useReceiptData/useBarcode hooks
- Add error handling for barcode generation

Tests:
- Unit: consumable tax category mapping, exempt rate short-circuit
- API: ready-for-pickup listing + search, from-repair transaction creation,
  consumable exclusion from line items, tax rate verification (labor=service,
  part=goods), duplicate prevention, ticket auto-pickup on payment completion,
  isConsumable product filter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
95cf017b4b feat: repair-POS integration, receipt formats, manager overrides, price adjustments
- Add thermal/full-page receipt format toggle (per-device, localStorage)
- Full-page receipt uses clean invoice layout matching repair PDF style
- Settings page reorganized into tabbed sections (Store, Locations, Modules, Receipt, POS Security, Advanced)
- Manager override system: configurable PIN prompt for void, refund, discount, cash in/out
- Discount threshold setting: require manager approval above X%
- Consumable product type: tracked for internal job costing, excluded from POS search, receipts, and customer-facing totals
- Repair line item dialog: product picker dropdown for parts/consumables from inventory
- Repair → POS checkout: load ready-for-pickup tickets into repair_payment transactions with proper tax categories (labor=service, parts=goods)
- Transaction completion auto-updates repair ticket status to picked_up
- POS Repairs dialog with Pickup and New Intake tabs, customer account lookup
- Inline price adjustment on cart items: % off, $ off, or set price with live preview
- Order-level discount button with same three input modes
- Backend: migration 0043 (consumable enum + is_consumable flag), createFromRepairTicket service, ready-for-pickup endpoint
- Fix: backend dev script uses --env-file for turbo compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
a48da03289 feat: orders lookup with receipt reprint, refresh stock after sale
- "Orders" button in POS quick actions shows recent transactions
- Search by transaction number, tap to view receipt, print or save PDF
- Product stock counts refresh after completing a sale
- Invalidate product search queries on payment completion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
e19cdc76e0 fix: dynamic PDF height based on receipt content length
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
fe40b563d5 fix: receipt uses inline styles for PDF/print compatibility, thermal width
- Replace all Tailwind classes with inline styles (fixes oklch color error in html2pdf)
- Narrow receipt to 260px / 10px font for 72mm thermal paper
- Print uses hidden iframe instead of window.open (fixes Safari about:blank)
- PDF canvas width matches thermal format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
49db60e31f feat: receipt PDF save and print via html2pdf.js
- Save PDF button downloads receipt directly
- Print button opens PDF in new window and triggers print dialog
- Replaces previous window.print() approach that lost styles
- Receipt generated on demand from transaction data, no file storage needed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
8820a56a51 feat: receipt customization settings tab with header, footer, policy, social
- New Receipt tab in Settings page with editable fields
- receipt_header: text below logo (e.g. tagline)
- receipt_footer: thank you message
- receipt_return_policy: return policy text
- receipt_social: website/social media
- All stored in app_config, rendered on printed receipts
- Seeded in migration with empty defaults

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
0aa9345c27 feat: show company logo on receipt if uploaded, fall back to name
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
3519db9bd9 feat: printable receipts with barcode on payment complete
- Receipt component with thermal (80mm) and full-page layout support
- Code 128 barcode from transaction number via JsBarcode
- Store name, address, line items, totals, payment info, barcode
- Print button on sale complete screen (browser print dialog)
- Email button placeholder (disabled, ready for SMTP integration)
- @media print CSS hides everything except receipt content
- Receipt data fetched from GET /transactions/:id/receipt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
0fd73015f7 fix: customer history query, seed transactions tied to accounts
- Fix customerHistoryOptions closure bug (historySearch was inaccessible)
- Pass itemSearch as parameter instead of capturing from outer scope
- Seed 5 completed transactions tied to accounts (Smith, Johnson, Garcia, Chen)
- Seed admin user with employee number 1001 and PIN 1234

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
d21972212b feat: customer lookup from POS with order history and item search
- Customer dialog in cart panel: search accounts by name, phone, email, account #
- Selected customer shown with name, phone, email in cart header
- accountId passed when creating transactions
- Order history view: tap a transaction to expand and see line items
- Item search in history (e.g. "strings") — filters orders containing that item
- Backend: add accountId and itemSearch filters to transaction list endpoint
- itemSearch uses EXISTS subquery on line item descriptions (ILIKE)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
cf299ac1d2 feat: POS PIN unlock with employee number + PIN auth
- Add employeeNumber and pinHash fields to users table
- POST /auth/pin-login: takes combined code (4-digit employee# + 4-digit PIN)
- POST /auth/set-pin: employee sets their own PIN (requires full auth)
- DELETE /auth/pin: remove PIN
- Lock screen with numpad, auto-submits on 8 digits, visual dot separator
- POS uses its own auth token separate from admin session
- Admin "POS" link clears admin session before navigating
- /pos route has no auth guard — lock screen is the auth
- API client uses POS token when available, admin token otherwise
- Auto-lock timer reads pos_lock_timeout from app_config (default 15 min)
- Lock button in POS top bar, shows current cashier name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
6505b2dcb9 fix: drawer open/close updates UI immediately without refresh
- Return null instead of throwing on 404 for drawer current query
- Sync drawer session ID to null when drawer closes
- Await query invalidation before closing dialog
- Fix unused approvedBy lint error

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
ryan
12a3e170de feat: add cash in/out UI and hide drawer balance from cashier
- Cash In / Cash Out buttons in drawer dialog when open
- Amount + reason form, adjustment history with IN/OUT badges
- Drawer badge shows "Drawer Open" without balance (manager info only)
- API helpers for addAdjustment and getAdjustments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:05:19 +00:00
Ryan Moon
254fe0e5d5 fix: move ts-expect-error inside navigate object to suppress search type error
All checks were successful
Build & Release / build (push) Successful in 1m6s
2026-04-05 10:42:00 -05:00
Ryan Moon
5750af83d8 fix: suppress navigate search type error in usePagination; fix setBillingUnit cast
Some checks failed
Build & Release / build (push) Failing after 35s
- use-pagination.ts: ts-expect-error on navigate call — search type resolves as never without route context, safe with strict:false
- $enrollmentId.tsx: wrap onValueChange to cast string to billingUnit union type

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 10:40:23 -05:00
Ryan Moon
505f8fd4e4 fix: correct TanStack Router search types for all navigate/Link calls
Some checks failed
Build & Release / build (push) Failing after 33s
Each destination route's search must match its validateSearch shape exactly:
- Detail pages (tab-based): { tab: '...' }
- List pages with extra filters: include status, instructorId, view, categoryId etc.
- Form pages (enrollments/new, repairs/new): include only their specific fields
- use-pagination.ts: fix search reducer to use (prev: any) instead of invalid cast

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 10:37:34 -05:00
Ryan Moon
a84530e80e fix: replace invalid TanStack Router search casts with typed defaults
Some checks failed
Build & Release / build (push) Failing after 32s
Newer TanStack Router enforces strict types on search params — 'search: {} as Record<string, unknown>' no longer satisfies routes with validateSearch. Replace all occurrences with the correct search shape for each destination route (pagination defaults for list routes, tab/field defaults for detail routes).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 10:33:36 -05:00
ryan
a0be16d848 fix: resolve all frontend lint errors and warnings
All checks were successful
CI / ci (pull_request) Successful in 21s
CI / e2e (pull_request) Successful in 59s
Replace all `any` types with proper types across 36 files:
- TanStack Router search params: `{} as Record<string, unknown>`
- API response pagination: proper typed interface
- DataTable column casts: remove unnecessary `as any`
- Function params and event handlers: use specific types
- Remove unused imports and variables in POS components

Frontend lint now passes with 0 errors and 0 warnings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 20:12:17 +00:00
ryan
1673e18fe8 fix: require open drawer to complete transactions, fix product price field
Some checks failed
CI / ci (pull_request) Failing after 20s
CI / e2e (pull_request) Has been skipped
- Backend enforces open drawer at location before completing any transaction
- Frontend disables payment buttons when drawer is closed with warning message
- Fix product price field name (price, not sellingPrice) in POS API types
- Fix seed UUIDs to use valid UUID v4 format (version nibble must be 1-8)
- Fix Vite allowedHosts for dev.lunarfront.tech access
- Add e2e test for drawer enforcement (39 POS tests now pass)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 19:54:07 +00:00
ryan
bd3a25aa1c feat: add POS register screen with full-screen touch-optimized layout
Standalone register at /pos bypassing the admin sidebar layout:
- Two-panel layout: product search/grid (60%) + cart/payment (40%)
- Product search with barcode scan support (UPC lookup on Enter)
- Custom item entry dialog for ad-hoc items
- Cart with line items, tax, totals, and remove-item support
- Payment dialogs: cash (quick amounts + change calc), card, check
- Drawer open/close with balance reconciliation and over/short
- Auto-creates pending transaction on first item added
- POS link added to admin sidebar nav (module-gated)
- Zustand store for POS session state, React Query for server data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 19:29:37 +00:00
ryan
aa5b53920d feat: add app configuration UI to settings page
All checks were successful
CI / ci (pull_request) Successful in 17s
CI / e2e (pull_request) Successful in 47s
Log level dropdown on the settings page lets admins change the application
log verbosity at runtime without restarting. Uses the new /v1/config API
with the existing settings.view/settings.edit permissions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 19:04:31 +00:00
Ryan Moon
05f926c0dc fix: remove unused imports and dead code to clear ESLint errors 2026-04-01 20:34:56 -05:00
Ryan Moon
bde3ad64fd Fix code review items: atomic qty increment, unit updatedAt, suppliers/all endpoint, SKU unique index 2026-03-31 05:08:01 -05:00
Ryan Moon
5f5ba9e4a2 Build inventory frontend and stock management features
- Full inventory UI: product list with search/filter, product detail with
  tabs (details, units, suppliers, stock receipts, price history)
- Product filters: category, type (serialized/rental/repair), low stock,
  active/inactive — all server-side with URL-synced state
- Product-supplier junction: link products to multiple suppliers with
  preferred flag, joined supplier details in UI
- Stock receipts: record incoming stock with supplier, qty, cost per unit,
  invoice number; auto-increments qty_on_hand for non-serialized products
- Price history tab on product detail page
- categories/all endpoint to avoid pagination limit on dropdown fetches
- categoryId filter on product list endpoint
- Repair parts and additional inventory items in music store seed data
- isDualUseRepair corrected: instruments set to false, strings/parts true
- Product-supplier links and stock receipts in seed data
- Price history seed data simulating cost increases over past year
- 37 API tests covering categories, suppliers, products, units,
  product-suppliers, and stock receipts
- alert-dialog and checkbox UI components
- sync-and-deploy.sh script for rsync + remote deploy
2026-03-30 20:12:07 -05:00
Ryan Moon
5ad27bc196 Add lessons module, rate cycles, EC2 deploy scripts, and help content
- Lessons module: lesson types, instructors, schedule slots, enrollments,
  sessions (list + week grid view), lesson plans, grading scales, templates
- Rate cycles: replace monthly_rate with billing_interval + billing_unit on
  enrollments; add weekly/monthly/quarterly rate presets to lesson types and
  schedule slots with auto-fill on enrollment form
- Member detail page: tabbed layout for details, identity documents, enrollments
- Sessions week view: custom 7-column grid replacing react-big-calendar
- Music store seed: instructors, lesson types, slots, enrollments, sessions,
  grading scale, lesson plan template
- Scrollbar styling: themed to match sidebar/app palette
- deploy/: EC2 setup and redeploy scripts, nginx config, systemd service
- Help: add Lessons category (overview, types, instructors, slots, enrollments,
  sessions, plans/grading); collapsible sidebar with independent scroll;
  remove POS/accounting references from docs
2026-03-30 18:52:57 -05:00
Ryan Moon
145eb0efce Improve sidebar: collapsible, grouped sections, scrollable nav, pinned footer
- Sidebar collapses to icon-only mode with toggle button
- Nav items grouped into sections (Customers, Repairs, Storage, Admin)
- Each section is independently collapsible
- Middle nav area scrolls when items overflow
- Help, profile, and sign out pinned to bottom
2026-03-30 08:52:23 -05:00
Ryan Moon
9400828f62 Rename Forte to LunarFront, generalize for any small business
Rebrand from Forte (music-store-specific) to LunarFront (any small business):
- Package namespace @forte/* → @lunarfront/*
- Database forte/forte_test → lunarfront/lunarfront_test
- Docker containers, volumes, connection strings
- UI branding, localStorage keys, test emails
- All documentation and planning docs

Generalize music-specific terminology:
- instrumentDescription → itemDescription
- instrumentCount → itemCount
- instrumentType → itemCategory (on service templates)
- New migration 0027_generalize_terminology for column renames
- Seed data updated with generic examples
- RBAC descriptions updated
2026-03-30 08:51:54 -05:00
Ryan Moon
a51f1f5141 Add vault category permissions dialog with role/user management 2026-03-30 07:23:23 -05:00
Ryan Moon
4438188362 Fix vault dialog autofill: prevent browser password manager from overriding dark theme colors 2026-03-30 07:18:58 -05:00
Ryan Moon
e346e072b8 Add module management system for enabling/disabling features
Stores can enable/disable feature modules from Settings. When disabled,
nav links are hidden and API routes return 403. Designed as the
foundation for future license-based gating (licensed + enabled flags).

Core modules (Accounts, Members, Users, Roles, Settings) are always on.

- module_config table with slug, name, description, licensed, enabled
- In-memory cache for fast per-request module checks
- requireModule middleware wraps route groups in main.ts
- Settings page Modules card with toggle switches
- Sidebar hides nav links for disabled modules
- Default modules seeded: inventory, pos, repairs, rentals, lessons,
  files, vault, email, reports
2026-03-30 06:52:27 -05:00
Ryan Moon
1f9297f533 Add vault secret manager frontend UI
Three-state page: not initialized → locked → unlocked.
Any user with vault.view can unlock (for store opening).
Admins can lock and change master password.

- Two-panel layout: categories on left, entries on right
- Entry reveal button shows decrypted value for 30s with copy
- Add/edit/delete entries and categories
- KeyRound icon in sidebar navigation
2026-03-30 06:17:58 -05:00
Ryan Moon
f998b16a3f Add traverse access level for folder navigation without file access
When a permission is set on a nested folder, traverse is automatically
granted on all ancestor folders so users can navigate to it. Traverse
only shows subfolders in listings — files are hidden. This prevents
orphaned permissions where a user has access to a nested folder but
can't reach it.

Hierarchy: traverse < view < edit < admin
2026-03-29 18:04:24 -05:00
Ryan Moon
51ca2ca683 Add folder permissions UI and WebDAV protocol support
Permissions UI:
- FolderPermissionsDialog component with public/private toggle,
  role/user permission management, and access level badges
- Integrated into file manager toolbar (visible for folder admins)
- Backend returns accessLevel in folder detail endpoint

WebDAV server:
- Full WebDAV protocol at /webdav/ with Basic Auth (existing credentials)
- PROPFIND, GET, PUT, DELETE, MKCOL, COPY, MOVE, LOCK/UNLOCK support
- Permission-checked against existing folder access model
- In-memory lock stubs for Windows client compatibility
- 22 API integration tests covering all operations

Also fixes canAccess to check folder creator (was missing).
2026-03-29 17:38:57 -05:00
Ryan Moon
cbbf2713a1 Enlarge logo upload area to 256x128 for better preview 2026-03-29 16:39:36 -05:00