diff --git a/admin/src/api/themes.ts b/admin/src/api/themes.ts index aab4eb6..ae9fa36 100644 --- a/admin/src/api/themes.ts +++ b/admin/src/api/themes.ts @@ -30,6 +30,8 @@ export interface ThemeSettings { export interface Theme { id: string name: string + icon?: string | null + description?: string | null isPublic: boolean colors: ThemeColors settings: ThemeSettings @@ -45,6 +47,8 @@ export interface Theme { export interface ThemePreview { id: string name: string + icon?: string | null + description?: string | null isPublic: boolean colors: ThemeColors settings: ThemeSettings @@ -57,6 +61,8 @@ export interface ThemePreview { export interface CreateThemeDto { name: string + icon?: string + description?: string isPublic?: boolean colors: ThemeColors settings: ThemeSettings @@ -64,6 +70,8 @@ export interface CreateThemeDto { export interface UpdateThemeDto { name?: string + icon?: string + description?: string isPublic?: boolean colors?: ThemeColors settings?: ThemeSettings diff --git a/admin/src/components/ThemeEditorDialog.tsx b/admin/src/components/ThemeEditorDialog.tsx index 07a87e6..755171b 100644 --- a/admin/src/components/ThemeEditorDialog.tsx +++ b/admin/src/components/ThemeEditorDialog.tsx @@ -25,6 +25,8 @@ interface ThemeEditorDialogProps { theme: ThemePreview | null onSave: (data: { name: string + icon?: string + description?: string isPublic: boolean colors: ThemeColors settings: ThemeSettings @@ -40,21 +42,107 @@ interface ColorFieldProps { description?: string } +// Extract hex color from rgba string +function extractHexFromRgba(rgba: string): string | null { + const match = rgba.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/) + if (match) { + const r = parseInt(match[1], 10).toString(16).padStart(2, '0') + const g = parseInt(match[2], 10).toString(16).padStart(2, '0') + const b = parseInt(match[3], 10).toString(16).padStart(2, '0') + return `#${r}${g}${b}`.toUpperCase() + } + return null +} + +// Extract first hex color from gradient +function extractHexFromGradient(gradient: string): string | null { + const hexMatch = gradient.match(/#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}/) + if (hexMatch) { + let hex = hexMatch[0] + // Convert 3-digit hex to 6-digit + if (hex.length === 4) { + hex = `#${hex[1]}${hex[1]}${hex[2]}${hex[2]}${hex[3]}${hex[3]}` + } + return hex.toUpperCase() + } + return null +} + +// Get hex color for color picker +function getHexForPicker(value: string): string { + if (value.startsWith('#')) { + // Ensure 6-digit hex for color picker + if (value.length === 4) { + const r = value[1] + const g = value[2] + const b = value[3] + return `#${r}${r}${g}${g}${b}${b}`.toUpperCase() + } + if (value.length === 7) { + return value.toUpperCase() + } + } + + if (value.includes('rgba') || value.includes('rgb')) { + const hex = extractHexFromRgba(value) + if (hex) return hex + } + + if (value.includes('gradient')) { + const hex = extractHexFromGradient(value) + if (hex) return hex + } + + return '#000000' +} + function ColorField({ label, value, onChange, description }: ColorFieldProps) { - const isGradient = value.includes('gradient') || value.includes('rgba') + const hexForPicker = getHexForPicker(value) + + const handleColorPickerChange = (hex: string) => { + if (value.includes('gradient')) { + // Replace first hex color in gradient, handle both 3 and 6 digit hex + // Match first occurrence of hex color (either 3 or 6 digit) + const hexMatch = value.match(/#[0-9a-fA-F]{3}(?![0-9a-fA-F])|#[0-9a-fA-F]{6}/) + if (hexMatch) { + const updated = value.replace(hexMatch[0], hex) + onChange(updated) + } else { + // If no hex found, just use the hex directly (shouldn't happen normally) + onChange(hex) + } + } else if (value.includes('rgba')) { + // Convert hex to rgba, preserve alpha + const r = parseInt(hex.slice(1, 3), 16) + const g = parseInt(hex.slice(3, 5), 16) + const b = parseInt(hex.slice(5, 7), 16) + // Extract alpha from rgba string + const alphaMatch = value.match(/rgba\([^)]+,\s*([^)]+)\)/) + const alpha = alphaMatch ? alphaMatch[1] : '1' + onChange(`rgba(${r}, ${g}, ${b}, ${alpha})`) + } else if (value.match(/^rgb\(/)) { + // Convert hex to rgb + const r = parseInt(hex.slice(1, 3), 16) + const g = parseInt(hex.slice(3, 5), 16) + const b = parseInt(hex.slice(5, 7), 16) + onChange(`rgb(${r}, ${g}, ${b})`) + } else { + // Direct hex color + onChange(hex) + } + } return (