feat(admin): users and permissions

Sprint 25.

New features/admin/users/ module, net-new /:lang/backoffice/users route +
Dashboard Quick Action.

- users: name, Telegram username, scope (marketplace vs office admin),
  role (inline change), status (active/invited/suspended), last login
- 4 built-in roles (owner/admin/editor/viewer) with flat permission lists
- invitations: email + role + scope form, pending list + revoke (no email
  actually sends - local record only)
- passwordless login confirmed already real (AdminAuthService Telegram QR,
  docs/BACKEND.md item 1) - linked, not reimplemented
- per-user mock session list (device/IP/last-active, revoke) - flagged as
  mock since the real AdminAuthService only ever tracks the current
  browser's session
- per-user audit log dialog (role/status changes), same pattern as
  Sprint 24's per-transaction audit, intentionally separate from the
  system-wide log planned for Sprint 26

docs/ADMIN.md + docs/BACKEND.md (new item 14) updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-15 11:05:21 +04:00
parent 7d65913245
commit 17adc9e9fd
12 changed files with 455 additions and 3 deletions

View File

@@ -0,0 +1,8 @@
.admin-users-page { display: grid; gap: 16px; padding: 16px; max-width: 1100px; margin: 0 auto; }
.card { display: grid; gap: 12px; padding: 16px; border: 1px solid var(--border-color, #d3dad9); border-radius: 16px; background: #fff; }
.card h2 { margin: 0; font-size: 1.1rem; }
.hint { margin: 0; color: var(--text-secondary, #6b7280); font-size: 0.85rem; }
.invite-form { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
select { min-height: 40px; padding: 0 10px; border: 1px solid var(--border-color, #d3dad9); border-radius: 10px; }
.actions { display: flex; gap: 8px; flex-wrap: wrap; }
@media (max-width: 640px) { .invite-form { flex-direction: column; align-items: stretch; } }