Files
lunarfront-app/packages/admin/src/lib/themes.ts
Ryan Moon 572af05a3f Add top-level members list, primary member on account, member move, combined create flows
- GET /v1/members with search across all members (includes account name)
- POST /members/:id/move with optional accountId (creates new account if omitted)
- primary_member_id on account table, auto-set when first member added
- isMinor flag on member create (manual override when no DOB provided)
- Account search now includes member names
- New account form includes primary contact fields, auto-generates name
- Members page in sidebar with global search
2026-03-28 09:08:06 -05:00

360 lines
11 KiB
TypeScript

export interface ThemeColors {
background: string
foreground: string
card: string
cardForeground: string
popover: string
popoverForeground: string
primary: string
primaryForeground: string
secondary: string
secondaryForeground: string
muted: string
mutedForeground: string
accent: string
accentForeground: string
destructive: string
destructiveForeground: string
border: string
input: string
ring: string
sidebar: string
sidebarForeground: string
sidebarPrimary: string
sidebarPrimaryForeground: string
sidebarAccent: string
sidebarAccentForeground: string
sidebarBorder: string
}
export interface ThemePreset {
name: string
label: string
light: ThemeColors
dark: ThemeColors
}
export const themes: ThemePreset[] = [
{
name: 'slate',
label: 'Slate',
light: {
background: '210 20% 97%',
foreground: '222 47% 11%',
card: '0 0% 100%',
cardForeground: '222 47% 11%',
popover: '0 0% 100%',
popoverForeground: '222 47% 11%',
primary: '215 25% 27%',
primaryForeground: '210 40% 98%',
secondary: '210 40% 94%',
secondaryForeground: '222 47% 11%',
muted: '210 40% 94%',
mutedForeground: '215 16% 47%',
accent: '210 40% 94%',
accentForeground: '222 47% 11%',
destructive: '0 72% 51%',
destructiveForeground: '210 40% 98%',
border: '214 32% 89%',
input: '214 32% 89%',
ring: '215 25% 27%',
sidebar: '210 25% 95%',
sidebarForeground: '215 16% 47%',
sidebarPrimary: '215 25% 27%',
sidebarPrimaryForeground: '210 40% 98%',
sidebarAccent: '210 40% 92%',
sidebarAccentForeground: '222 47% 11%',
sidebarBorder: '214 32% 89%',
},
dark: {
background: '222 30% 8%',
foreground: '210 20% 86%',
card: '222 30% 10%',
cardForeground: '210 20% 86%',
popover: '222 30% 10%',
popoverForeground: '210 20% 86%',
primary: '215 20% 72%',
primaryForeground: '222 30% 8%',
secondary: '217 20% 15%',
secondaryForeground: '210 20% 86%',
muted: '217 20% 15%',
mutedForeground: '215 15% 55%',
accent: '217 20% 15%',
accentForeground: '210 20% 86%',
destructive: '0 55% 48%',
destructiveForeground: '210 20% 92%',
border: '217 20% 17%',
input: '217 20% 17%',
ring: '215 20% 55%',
sidebar: '222 30% 7%',
sidebarForeground: '215 15% 65%',
sidebarPrimary: '215 20% 72%',
sidebarPrimaryForeground: '0 0% 100%',
sidebarAccent: '217 20% 13%',
sidebarAccentForeground: '210 20% 86%',
sidebarBorder: '217 20% 15%',
},
},
{
name: 'emerald',
label: 'Emerald',
light: {
background: '150 20% 97%',
foreground: '150 40% 10%',
card: '0 0% 100%',
cardForeground: '150 40% 10%',
popover: '0 0% 100%',
popoverForeground: '150 40% 10%',
primary: '160 84% 29%',
primaryForeground: '150 20% 98%',
secondary: '150 20% 93%',
secondaryForeground: '150 40% 10%',
muted: '150 20% 93%',
mutedForeground: '150 10% 45%',
accent: '150 20% 93%',
accentForeground: '150 40% 10%',
destructive: '0 72% 51%',
destructiveForeground: '150 20% 98%',
border: '150 15% 88%',
input: '150 15% 88%',
ring: '160 84% 29%',
sidebar: '150 20% 95%',
sidebarForeground: '150 10% 45%',
sidebarPrimary: '160 84% 29%',
sidebarPrimaryForeground: '150 20% 98%',
sidebarAccent: '150 20% 91%',
sidebarAccentForeground: '150 40% 10%',
sidebarBorder: '150 15% 88%',
},
dark: {
background: '155 30% 7%',
foreground: '150 15% 85%',
card: '155 30% 9%',
cardForeground: '150 15% 85%',
popover: '155 30% 9%',
popoverForeground: '150 15% 85%',
primary: '160 72% 42%',
primaryForeground: '155 30% 7%',
secondary: '155 20% 14%',
secondaryForeground: '150 15% 85%',
muted: '155 20% 14%',
mutedForeground: '150 10% 52%',
accent: '155 20% 14%',
accentForeground: '150 15% 85%',
destructive: '0 55% 48%',
destructiveForeground: '150 15% 92%',
border: '155 20% 16%',
input: '155 20% 16%',
ring: '160 72% 42%',
sidebar: '155 30% 6%',
sidebarForeground: '150 10% 62%',
sidebarPrimary: '160 72% 42%',
sidebarPrimaryForeground: '0 0% 100%',
sidebarAccent: '155 20% 12%',
sidebarAccentForeground: '150 15% 85%',
sidebarBorder: '155 20% 14%',
},
},
{
name: 'violet',
label: 'Violet',
light: {
background: '260 15% 97%',
foreground: '260 40% 11%',
card: '0 0% 100%',
cardForeground: '260 40% 11%',
popover: '0 0% 100%',
popoverForeground: '260 40% 11%',
primary: '262 83% 58%',
primaryForeground: '260 15% 98%',
secondary: '260 20% 94%',
secondaryForeground: '260 40% 11%',
muted: '260 20% 94%',
mutedForeground: '260 10% 47%',
accent: '260 20% 94%',
accentForeground: '260 40% 11%',
destructive: '0 72% 51%',
destructiveForeground: '260 15% 98%',
border: '260 15% 89%',
input: '260 15% 89%',
ring: '262 83% 58%',
sidebar: '260 18% 95%',
sidebarForeground: '260 10% 47%',
sidebarPrimary: '262 83% 58%',
sidebarPrimaryForeground: '260 15% 98%',
sidebarAccent: '260 20% 92%',
sidebarAccentForeground: '260 40% 11%',
sidebarBorder: '260 15% 89%',
},
dark: {
background: '260 30% 8%',
foreground: '260 15% 86%',
card: '260 30% 10%',
cardForeground: '260 15% 86%',
popover: '260 30% 10%',
popoverForeground: '260 15% 86%',
primary: '262 83% 65%',
primaryForeground: '260 30% 8%',
secondary: '260 20% 15%',
secondaryForeground: '260 15% 86%',
muted: '260 20% 15%',
mutedForeground: '260 10% 54%',
accent: '260 20% 15%',
accentForeground: '260 15% 86%',
destructive: '0 55% 48%',
destructiveForeground: '260 15% 92%',
border: '260 20% 17%',
input: '260 20% 17%',
ring: '262 83% 65%',
sidebar: '260 30% 7%',
sidebarForeground: '260 10% 64%',
sidebarPrimary: '262 83% 65%',
sidebarPrimaryForeground: '0 0% 100%',
sidebarAccent: '260 20% 13%',
sidebarAccentForeground: '260 15% 86%',
sidebarBorder: '260 20% 15%',
},
},
{
name: 'amber',
label: 'Amber',
light: {
background: '36 20% 97%',
foreground: '28 40% 10%',
card: '0 0% 100%',
cardForeground: '28 40% 10%',
popover: '0 0% 100%',
popoverForeground: '28 40% 10%',
primary: '25 95% 40%',
primaryForeground: '36 20% 98%',
secondary: '36 25% 93%',
secondaryForeground: '28 40% 10%',
muted: '36 25% 93%',
mutedForeground: '28 10% 47%',
accent: '36 25% 93%',
accentForeground: '28 40% 10%',
destructive: '0 72% 51%',
destructiveForeground: '36 20% 98%',
border: '36 20% 88%',
input: '36 20% 88%',
ring: '25 95% 40%',
sidebar: '36 22% 95%',
sidebarForeground: '28 10% 47%',
sidebarPrimary: '25 95% 40%',
sidebarPrimaryForeground: '36 20% 98%',
sidebarAccent: '36 25% 91%',
sidebarAccentForeground: '28 40% 10%',
sidebarBorder: '36 20% 88%',
},
dark: {
background: '28 25% 7%',
foreground: '36 15% 85%',
card: '28 25% 9%',
cardForeground: '36 15% 85%',
popover: '28 25% 9%',
popoverForeground: '36 15% 85%',
primary: '30 90% 52%',
primaryForeground: '28 25% 7%',
secondary: '28 18% 14%',
secondaryForeground: '36 15% 85%',
muted: '28 18% 14%',
mutedForeground: '28 10% 52%',
accent: '28 18% 14%',
accentForeground: '36 15% 85%',
destructive: '0 55% 48%',
destructiveForeground: '36 15% 92%',
border: '28 18% 16%',
input: '28 18% 16%',
ring: '30 90% 52%',
sidebar: '28 25% 6%',
sidebarForeground: '28 10% 62%',
sidebarPrimary: '30 90% 52%',
sidebarPrimaryForeground: '0 0% 100%',
sidebarAccent: '28 18% 12%',
sidebarAccentForeground: '36 15% 85%',
sidebarBorder: '28 18% 14%',
},
},
{
name: 'rose',
label: 'Rose',
light: {
background: '350 15% 97%',
foreground: '350 40% 10%',
card: '0 0% 100%',
cardForeground: '350 40% 10%',
popover: '0 0% 100%',
popoverForeground: '350 40% 10%',
primary: '347 77% 50%',
primaryForeground: '350 15% 98%',
secondary: '350 20% 94%',
secondaryForeground: '350 40% 10%',
muted: '350 20% 94%',
mutedForeground: '350 10% 47%',
accent: '350 20% 94%',
accentForeground: '350 40% 10%',
destructive: '0 72% 51%',
destructiveForeground: '350 15% 98%',
border: '350 15% 89%',
input: '350 15% 89%',
ring: '347 77% 50%',
sidebar: '350 18% 95%',
sidebarForeground: '350 10% 47%',
sidebarPrimary: '347 77% 50%',
sidebarPrimaryForeground: '350 15% 98%',
sidebarAccent: '350 20% 92%',
sidebarAccentForeground: '350 40% 10%',
sidebarBorder: '350 15% 89%',
},
dark: {
background: '350 25% 7%',
foreground: '350 12% 86%',
card: '350 25% 9%',
cardForeground: '350 12% 86%',
popover: '350 25% 9%',
popoverForeground: '350 12% 86%',
primary: '347 77% 58%',
primaryForeground: '350 25% 7%',
secondary: '350 18% 14%',
secondaryForeground: '350 12% 86%',
muted: '350 18% 14%',
mutedForeground: '350 10% 54%',
accent: '350 18% 14%',
accentForeground: '350 12% 86%',
destructive: '0 55% 48%',
destructiveForeground: '350 12% 92%',
border: '350 18% 16%',
input: '350 18% 16%',
ring: '347 77% 58%',
sidebar: '350 25% 6%',
sidebarForeground: '350 10% 62%',
sidebarPrimary: '347 77% 58%',
sidebarPrimaryForeground: '0 0% 100%',
sidebarAccent: '350 18% 12%',
sidebarAccentForeground: '350 12% 86%',
sidebarBorder: '350 18% 14%',
},
},
]
function camelToKebab(str: string): string {
return str.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase())
}
export function applyThemeColors(colors: ThemeColors) {
const root = document.documentElement
for (const [key, value] of Object.entries(colors)) {
const prop = `--${camelToKebab(key)}`
const hsl = `hsl(${value})`
root.style.setProperty(prop, hsl)
}
// Also set on body for portaled components (dialogs, dropdowns, popovers)
for (const [key, value] of Object.entries(colors)) {
document.body.style.setProperty(`--${camelToKebab(key)}`, `hsl(${value})`)
}
}
export function getThemeByName(name: string): ThemePreset {
return themes.find((t) => t.name === name) ?? themes[0]
}