Files
Ronni Baslund 0bd4e5498e feat: portal redesign, pricing catalog, partner-staff invites
- portal: new admin/ and partner/ surfaces with full component library
  (AppLauncher, Avatar, Badge, Card, Modal, Tabs, etc.), composables,
  layouts, partner-routing middleware, and supporting server APIs
- pricing: Price schema/module with operator CRUD, pricing.vue catalog UI,
  Subscription extended with cycle/currency/perSeatAmount/seats snapshots
  for stable MRR aggregation
- partner staff: User.partnerId, invite-partner-user DTO and flow,
  /partners/:slug/users endpoints, InvitePartnerUserModal, shared
  dezky-partner-staff Authentik group
- /me: partner-aware endpoint returning user + partner context so portal
  can route between end-user and partner-admin surfaces
- tenant: seats field for portfolio displays and future MRR calculations
- operator: pricing page, signed-out page, useMe/useToast composables,
  ToastStack
2026-05-28 20:00:33 +02:00

109 lines
8.4 KiB
TypeScript

// Fixtures for the end-user dashboard, profile, devices, security, etc.
export const currentUser = {
name: 'Anne Hansen',
email: 'anne@baslund.dk',
role: 'Customer admin',
title: 'CEO',
department: 'Leadership',
manager: '—',
timezone: 'Europe/Copenhagen',
language: 'da-DK',
joined: '2024-09-12',
}
// App tiles for the end-user dashboard. Source design has 4 fixed tiles with
// a short monospace badge line below the name (no separate hrefs row).
export const appTiles = [
{ key: 'mail', name: 'Mail', badge: '12 unread' },
{ key: 'drev', name: 'Drev', badge: '5 shared' },
{ key: 'moder', name: 'Møder', badge: '3 today' },
{ key: 'chat', name: 'Chat', badge: '2 mentions' },
]
// Single-time meeting rows. Source design uses a single time + a relative
// "in X" countdown + a "with" line.
export const todayAgenda = [
{ id: 't-1', time: '10:30', title: 'Sprint planning', with: 'Engineering · 6 people', in: '14 min' },
{ id: 't-2', time: '13:00', title: 'Customer demo · Novo', with: 'Frederik + Anne', in: '2 h 44 min' },
{ id: 't-3', time: '15:30', title: '1:1 with Mikkel', with: 'Mikkel Nørgaard', in: '5 h 14 min' },
]
export const recentFiles = [
{ id: 'f-1', name: 'Q3 board deck.key', path: 'Drev · /Board', updated: '12 min ago', size: '24 MB' },
{ id: 'f-2', name: 'Pricing v3.xlsx', path: 'Drev · /Finance', updated: '1 h ago', size: '482 KB' },
{ id: 'f-3', name: 'Brand guide.pdf', path: 'Drev · /Brand', updated: '3 h ago', size: '8.1 MB' },
{ id: 'f-4', name: 'Customer interviews.docx', path: 'Drev · /Research', updated: 'Yesterday', size: '120 KB' },
{ id: 'f-5', name: 'Roadmap 2026.fig', path: 'Drev · /Design', updated: '2 d ago', size: '34 MB' },
]
// 6 hardcoded pending items — source design's exact list. Tone determines
// the icon-tint + whether the CTA is primary (only 'bad' gets primary).
export const needsAttention = [
{ id: 't1', icon: 'shield', tone: 'bad', title: 'Complete MFA enrolment', hint: 'overdue · required by 1 Jun', cta: 'Set up', target: 'security' },
{ id: 't2', icon: 'chat', tone: 'info', title: 'Sofie mentioned you in #design', hint: '2 h ago', cta: 'Reply', target: 'chat' },
{ id: 't3', icon: 'folder', tone: 'info', title: 'Frederik shared "Q3 forecast.xlsx"', hint: 'yesterday', cta: 'Review', target: 'file' },
{ id: 't4', icon: 'card', tone: 'warn', title: 'Approve expense report · 1.840 DKK', hint: '3 days', cta: 'Approve', target: 'expense' },
{ id: 't5', icon: 'users', tone: 'info', title: 'Schedule 1:1 with Mikkel', hint: 'overdue by 5 days', cta: 'Schedule', target: 'meeting' },
{ id: 't6', icon: 'file', tone: 'info', title: 'Take Q2 customer feedback survey', hint: 'open until Friday', cta: 'Open', target: 'survey' },
] as const
// Devices match source `DEVICES` from platform-enduser.jsx · grouped into
// laptop / phone / tablet ("desktop" in source maps to our `laptop` kind).
export const devices = [
{ id: 'd1', label: 'MacBook Pro · 14"', kind: 'laptop', os: 'macOS 14.4', app: 'Chrome 132', location: 'Copenhagen, DK', ip: '92.43.118.4', lastActive: 'now', current: true, trusted: false, stale: false },
{ id: 'd2', label: 'iPhone 15 Pro', kind: 'phone', os: 'iOS 18.1', app: 'dezky Mail · 2.4', location: 'Copenhagen, DK', ip: '92.43.118.4', lastActive: '14 min ago', current: false, trusted: false, stale: false },
{ id: 'd3', label: 'iPhone 15 Pro', kind: 'phone', os: 'iOS 18.1', app: 'dezky Drev · 2.1', location: 'Copenhagen, DK', ip: '92.43.118.4', lastActive: '14 min ago', current: false, trusted: false, stale: false },
{ id: 'd4', label: 'MacBook Air · 13"', kind: 'laptop', os: 'macOS 14.4', app: 'Safari 17.4', location: 'Aarhus, DK', ip: '78.110.4.92', lastActive: '2 d ago', current: false, trusted: false, stale: false },
{ id: 'd5', label: 'iPad Pro · 11"', kind: 'tablet', os: 'iPadOS 18', app: 'dezky · web', location: 'Copenhagen, DK', ip: '92.43.118.4', lastActive: '5 d ago', current: false, trusted: false, stale: false },
{ id: 'd6', label: 'Linux workstation', kind: 'laptop', os: 'Ubuntu 24.04', app: 'Firefox 134', location: 'Copenhagen, DK', ip: '10.0.4.18', lastActive: '11 d ago', current: false, trusted: false, stale: true },
]
// MFA methods mirror source `MFA_METHODS`. Source has TouchID as primary
// (first webauthn), YubiKey, and 1Password TOTP.
export const mfaMethods = [
{ id: 'm1', kind: 'webauthn' as const, label: 'MacBook Pro · Touch ID', enrolledOn: '14 Jan 2026', lastUsed: '2 min ago', primary: true },
{ id: 'm2', kind: 'webauthn' as const, label: 'YubiKey 5C · work', enrolledOn: '14 Jan 2026', lastUsed: '3 d ago', primary: false },
{ id: 'm3', kind: 'totp' as const, label: '1Password', enrolledOn: '02 Feb 2026', lastUsed: '4 d ago', primary: false },
]
export const recoveryCodes = [
'whkz-86p4', 'jt2m-c98n', 'q4dx-r58y', 'b1nv-7gpx',
'fm8w-22qr', 'k5xt-pa9c', 'd91j-8mwz', 'rv6t-3hyn',
'snk2-9b8d', '7zfp-q4mv',
]
// Sign-in history mirrors source `SIGNIN_HISTORY` — includes the failed
// attempt from 203.0.113.4 used for the warning callout.
export const signInHistory = [
{ id: 'si-1', when: '14:02 today', ip: '92.43.118.4', location: 'Copenhagen, DK', ua: 'Chrome 132 · macOS', method: 'webauthn', result: 'ok' },
{ id: 'si-2', when: '11:48 today', ip: '92.43.118.4', location: 'Copenhagen, DK', ua: 'dezky Mail · iOS', method: 'session', result: 'ok' },
{ id: 'si-3', when: '09:21 today', ip: '92.43.118.4', location: 'Copenhagen, DK', ua: 'Chrome 132 · macOS', method: 'webauthn', result: 'ok' },
{ id: 'si-4', when: 'Yesterday 22:14', ip: '78.110.4.92', location: 'Aarhus, DK', ua: 'Safari 17.4 · macOS', method: 'totp', result: 'ok' },
{ id: 'si-5', when: 'Yesterday 18:02', ip: '203.0.113.4', location: 'Unknown', ua: 'Chrome 132 · Windows', method: 'password', result: 'failed', reason: 'Wrong password · 3 attempts' },
{ id: 'si-6', when: '2 d ago 14:22', ip: '78.110.4.92', location: 'Aarhus, DK', ua: 'Safari 17.4 · macOS', method: 'webauthn', result: 'ok' },
{ id: 'si-7', when: '4 d ago 09:08', ip: '92.43.118.4', location: 'Copenhagen, DK', ua: 'Firefox 134 · Linux', method: 'totp', result: 'ok' },
]
// KB articles mirror source `KB_ARTICLES` exactly. `read` is the read-time
// string ("4 min"), `popular` flags the items rendered in the Popular row.
export const helpArticles = [
{ id: 'kb1', title: 'Setting up MFA for your workspace', category: 'Getting started', read: '4 min', popular: true },
{ id: 'kb2', title: 'Migrating from Microsoft 365 to Dezky', category: 'Migration', read: '12 min', popular: true },
{ id: 'kb3', title: 'Configuring SAML SSO for external apps', category: 'Identity', read: '8 min', popular: false },
{ id: 'kb4', title: 'Custom domain (CNAME) setup', category: 'Branding', read: '6 min', popular: false },
{ id: 'kb5', title: 'Understanding storage quotas', category: 'Drev', read: '5 min', popular: false },
{ id: 'kb6', title: 'OIOUBL invoicing for Danish customers', category: 'Billing', read: '7 min', popular: false },
{ id: 'kb7', title: 'API tokens and webhooks', category: 'Developers', read: '10 min', popular: false },
{ id: 'kb8', title: 'GDPR data export requests', category: 'Compliance', read: '5 min', popular: false },
]
// Tickets mirror source `TICKETS`. Status uses the source's three-bucket
// vocabulary ("awaiting customer" / "in progress" / "resolved").
export const myTickets = [
{ id: 'TKT-2841', title: 'Cannot share folder externally', status: 'awaiting customer', severity: 'P3', age: '2 d', last: '2 h ago', updates: 4 },
{ id: 'TKT-2832', title: 'Mobile app missing meeting recordings', status: 'in progress', severity: 'P3', age: '5 d', last: '1 d ago', updates: 8 },
{ id: 'TKT-2701', title: 'How do I configure SAML for Notion?', status: 'resolved', severity: 'P3', age: '12 d', last: '8 d ago', updates: 6 },
{ id: 'TKT-2650', title: 'Bulk export users to Active Directory', status: 'resolved', severity: 'P3', age: '18 d', last: '14 d ago', updates: 11 },
]