Files
fotospiel-app/resources/js/admin/mobile/theme.ts
Codex Agent 40bed1e44e
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled
feat(admin): modernize tenant admin PWA with cockpit layout and slate theme
- Replaced rainbow grid with phase-aware cockpit layout
- Implemented smart lifecycle hero with readiness logic
- Introduced dark command bar header with context pill and search placeholder
- Updated global Tamagui theme to slate/indigo palette
- Refined bottom navigation with minimalist spotlight style
2026-01-17 14:46:19 +01:00

143 lines
5.1 KiB
TypeScript

import { useTheme } from '@tamagui/core';
export const ADMIN_COLORS = {
primary: '#4F46E5', // Indigo 600
primaryStrong: '#4338CA', // Indigo 700
accent: '#F43F5E', // Rose 500
accentSoft: '#E0E7FF', // Indigo 100
accentWarm: '#FFE4E6', // Rose 100
warning: '#F59E0B', // Amber 500
success: '#10B981', // Emerald 500
danger: '#EF4444', // Red 500
text: '#0F172A', // Slate 900
textMuted: '#475569', // Slate 600
textSubtle: '#64748B', // Slate 500
border: '#E2E8F0', // Slate 200
surface: '#FFFFFF',
surfaceMuted: '#F8FAFC', // Slate 50
backdrop: '#0F172A',
};
export const ADMIN_ACTION_COLORS = {
settings: '#64748B', // Slate 500
tasks: '#F43F5E', // Rose 500 (Accent)
qr: '#10B981', // Emerald 500
images: '#4F46E5', // Indigo 600 (Primary)
liveShow: '#F59E0B', // Amber 500
liveShowSettings: '#6366F1', // Indigo 500
guests: '#334155', // Slate 700
guestMessages: '#4F46E5',
branding: '#6366F1',
photobooth: '#8B5CF6', // Violet 500
recap: '#94A3B8',
packages: '#4F46E5',
analytics: '#10B981',
invites: '#10B981',
};
export const ADMIN_GRADIENTS = {
primaryCta: `linear-gradient(135deg, ${ADMIN_COLORS.primary}, ${ADMIN_COLORS.primaryStrong})`,
softCard: 'linear-gradient(145deg, rgba(255,255,255,1), rgba(248,250,252,0.95))',
loginBackground: 'linear-gradient(135deg, #0F172A, #1E293B, #0F172A)',
appBackground: 'linear-gradient(180deg, #F1F5F9 0%, #F1F5F9 100%)',
appBackgroundDark: 'linear-gradient(180deg, #0F172A 0%, #1E293B 100%)',
};
export const ADMIN_MOTION = {
tileStaggerMs: 40,
};
type Rgb = { r: number; g: number; b: number };
function parseRgb(color: string): Rgb | null {
const trimmed = color.trim();
if (trimmed.startsWith('#')) {
const hex = trimmed.slice(1);
const normalized = hex.length === 3 ? hex.split('').map((ch) => ch + ch).join('') : hex;
if (normalized.length === 6) {
return {
r: Number.parseInt(normalized.slice(0, 2), 16),
g: Number.parseInt(normalized.slice(2, 4), 16),
b: Number.parseInt(normalized.slice(4, 6), 16),
};
}
}
const rgb = trimmed.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/i);
if (rgb) {
return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]) };
}
const rgba = trimmed.match(/^rgba\((\d+),\s*(\d+),\s*(\d+),\s*([0-9.]+)\)$/i);
if (rgba) {
return { r: Number(rgba[1]), g: Number(rgba[2]), b: Number(rgba[3]) };
}
return null;
}
export function withAlpha(color: string, alpha: number): string {
const rgb = parseRgb(color);
if (! rgb) {
return color;
}
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha})`;
}
function isDarkColor(color: string): boolean {
const rgb = parseRgb(color);
if (! rgb) {
return false;
}
const luminance = (0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b) / 255;
return luminance < 0.5;
}
export function useAdminTheme() {
const theme = useTheme();
const background = String(theme.background?.val ?? ADMIN_COLORS.surfaceMuted);
const surface = String(theme.surface?.val ?? ADMIN_COLORS.surface);
const border = String(theme.borderColor?.val ?? ADMIN_COLORS.border);
const isDark = isDarkColor(background);
const glassSurface = withAlpha(surface, isDark ? 0.96 : 0.98);
const glassSurfaceStrong = withAlpha(surface, isDark ? 1 : 1);
const glassBorder = withAlpha(border, isDark ? 0.85 : 0.95);
const glassShadow = isDark ? 'rgba(0, 0, 0, 0.55)' : 'rgba(15, 23, 42, 0.08)';
const appBackground = isDark ? ADMIN_GRADIENTS.appBackgroundDark : ADMIN_GRADIENTS.appBackground;
return {
theme,
background,
surface,
surfaceMuted: String(theme.muted?.val ?? ADMIN_COLORS.surfaceMuted),
border,
text: String(theme.color?.val ?? ADMIN_COLORS.text),
textStrong: String(theme.text?.val ?? theme.color?.val ?? ADMIN_COLORS.text),
muted: String(theme.muted?.val ?? ADMIN_COLORS.textMuted),
subtle: String(theme.textSubtle?.val ?? ADMIN_COLORS.textSubtle),
primary: String(theme.primary?.val ?? ADMIN_COLORS.primary),
accent: String(theme.accent?.val ?? ADMIN_COLORS.accent),
accentSoft: String(theme.blue3?.val ?? ADMIN_COLORS.accentSoft),
accentStrong: String(theme.blue11?.val ?? ADMIN_COLORS.primaryStrong),
successBg: String(theme.backgroundStrong?.val ?? '#DCFCE7'),
successText: String(theme.green10?.val ?? '#166534'),
dangerBg: String(theme.red3?.val ?? '#FEE2E2'),
dangerText: String(theme.red11?.val ?? ADMIN_COLORS.danger),
warningBg: String(theme.yellow3?.val ?? '#FEF3C7'),
warningBorder: String(theme.yellow6?.val ?? '#FCD34D'),
warningText: String(theme.yellow11?.val ?? '#B45309'),
infoBg: String(theme.blue3?.val ?? ADMIN_COLORS.accentSoft),
infoText: String(theme.blue10?.val ?? ADMIN_COLORS.primaryStrong),
danger: String(theme.danger?.val ?? ADMIN_COLORS.danger),
backdrop: String(theme.backgroundStrong?.val ?? ADMIN_COLORS.backdrop),
overlay: withAlpha(String(theme.backgroundStrong?.val ?? ADMIN_COLORS.backdrop), 0.6),
shadow: String(theme.shadowColor?.val ?? 'rgba(15, 23, 42, 0.08)'),
glassSurface,
glassSurfaceStrong,
glassBorder,
glassShadow,
appBackground,
};
}