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>
This commit is contained in:
ryan
2026-04-04 20:59:09 +00:00
parent 6505b2dcb9
commit cf299ac1d2
13 changed files with 396 additions and 39 deletions

View File

@@ -0,0 +1,16 @@
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "pin_hash" varchar(255);
ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "employee_number" varchar(20) UNIQUE;
-- Auto-assign employee numbers to existing users
DO $$ DECLARE r RECORD; num INT := 1001;
BEGIN
FOR r IN (SELECT id FROM "user" WHERE employee_number IS NULL ORDER BY created_at) LOOP
UPDATE "user" SET employee_number = num::text WHERE id = r.id;
num := num + 1;
END LOOP;
END $$;
-- Seed POS lock timeout config
INSERT INTO "app_config" ("key", "value", "description")
VALUES ('pos_lock_timeout', '15', 'POS auto-lock timeout in minutes (0 to disable)')
ON CONFLICT ("key") DO NOTHING;

View File

@@ -302,6 +302,13 @@
"when": 1775590000000,
"tag": "0042_drawer-adjustments",
"breakpoints": true
},
{
"idx": 42,
"version": "7",
"when": 1775590000000,
"tag": "0042_user-pin",
"breakpoints": true
}
]
}