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
This commit is contained in:
Ronni Baslund
2026-05-28 20:00:33 +02:00
parent be430179d9
commit 0bd4e5498e
144 changed files with 22110 additions and 209 deletions
+9
View File
@@ -25,3 +25,12 @@ button {
a {
color: inherit;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: rgba(128, 128, 128, 0.25); border-radius: 6px; }
::-webkit-scrollbar-track { background: transparent; }
:focus-visible {
outline: 2px solid var(--signal);
outline-offset: 2px;
}
+32 -6
View File
@@ -1,8 +1,9 @@
/* Dezky design tokens — workspace surface (light/bone).
Ported from project/platform-tokens.jsx (THEMES.light + ACCENTS.signal). */
/* Dezky portal design tokens.
Mirrors apps/operator/assets/styles/tokens.css (which ports project/platform-tokens.jsx
THEMES + ACCENTS + DENSITIES) so both portals share a visual vocabulary. */
:root {
/* Surface */
/* Surface — light/bone */
--bg: #F4F3EE; /* bone */
--surface: #FAFAF7; /* paper */
--elevated: #FFFFFF;
@@ -13,14 +14,20 @@
--text: #0A0A0A; /* carbon */
--text-dim: rgba(10, 10, 10, 0.55);
--text-mute: rgba(10, 10, 10, 0.4);
--row-hover: rgba(10, 10, 10, 0.03);
/* Sidebar (always-dark for brand consistency) */
/* Sidebar always-dark for brand presence in light mode */
--side-bg: #0A0A0A;
--side-surf: #141413;
--side-border: #1F1F1C;
--side-text: #F4F3EE;
--side-dim: rgba(244, 243, 238, 0.55);
--side-mute: rgba(244, 243, 238, 0.35);
--side-hover: rgba(244, 243, 238, 0.06);
--side-active: rgba(244, 243, 238, 0.1);
/* Brand accent */
--accent: #D4FF3A; /* signal */
/* Brand accent — defaults to Signal yellow */
--accent: #D4FF3A;
--accent-fg: #0A0A0A;
--signal: #D4FF3A;
@@ -35,6 +42,11 @@
--font-display: 'Inter Tight', 'Inter', sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, 'Menlo', monospace;
/* Density (defaults to comfy) */
--row-h: 56px;
--pad: 24px;
--gap: 20px;
/* Field input surface */
--input-bg: var(--bg);
}
@@ -48,9 +60,23 @@
--text: #F4F3EE;
--text-dim: rgba(244, 243, 238, 0.72);
--text-mute: rgba(244, 243, 238, 0.45);
--row-hover: rgba(244, 243, 238, 0.04);
--ok: #34C77B;
--warn: #F0B14A;
--bad: #F05858;
--info: #4D8BE8;
--input-bg: rgba(244, 243, 238, 0.04);
}
[data-density='compact'] {
--row-h: 44px;
--pad: 16px;
--gap: 14px;
}
/* Accent presets — toggled via TweaksPanel for the whitelabel preview. The
`--signal` brand-locked node-dot stays the same; only `--accent` flexes. */
[data-accent='signal'] { --accent: #D4FF3A; --accent-fg: #0A0A0A; }
[data-accent='cobalt'] { --accent: #3F6BFF; --accent-fg: #FFFFFF; }
[data-accent='coral'] { --accent: #FF6B4A; --accent-fg: #FFFFFF; }
[data-accent='moss'] { --accent: #5B8C5A; --accent-fg: #FFFFFF; }