Files
picpeak/frontend/src/hooks/useGalleryCustomCss.ts
T
Paul Nothaft 77a4bfd499 feat: implement 4 new features with bug fixes and refactoring plan
## Features Implemented

### 1. Event Rename Functionality
- Add EventRenameDialog component with live slug preview
- Create eventRenameService for safe event renaming
- Add slug_redirects table for old URL redirects
- Support optional email notification on rename
- Fix date formatting in slug (YYYY-MM-DD format)

### 2. Optional Event Contact Fields
- Add settings to make customer name/email/admin email optional
- Create migration for field requirement settings
- Update CreateEventPage forms to show "(optional)" labels
- Fix boolean parsing in publicSettings.js

### 3. Photo Filtering & Export
- Add PhotoFilterPanel with rating/likes/favorites/comments filters
- Create PhotoExportMenu with ZIP/metadata/XMP export options
- Add photoExportService with Lightroom XMP sidecar generation
- Create photoFilterBuilder utility for query construction
- Wire up photo selection to export button via onSelectionChange

### 4. Custom CSS Gallery Templates
- Add CssTemplateEditor component with 3 template slots
- Create cssSanitizer utility blocking XSS vectors
- Add gallery CSS endpoint for template delivery
- Integrate Custom CSS tab into Settings page
- Include default "Elegant Dark" template

## Bug Fixes
- Fix event rename date formatting (was showing full Date string)
- Fix common.optional translation key missing in locales
- Fix photo export button staying disabled when photos selected
- Fix authService import missing in SettingsPage

## Documentation
- Add comprehensive REFACTORING_PLAN.md for codebase improvement
- Add test specification documents for all features
- Add feature documentation for CSS templates

## Database Migrations
- 049_add_slug_redirects.js
- 050_add_optional_event_fields_settings.js
- 051_add_photo_filter_indexes.js
- 052_add_css_templates.js
2026-01-02 09:56:19 +01:00

67 lines
1.7 KiB
TypeScript

import { useEffect, useState } from 'react';
import { cssTemplatesService } from '../services/cssTemplates.service';
/**
* Hook to load and inject custom CSS for a gallery
* @param slug - Gallery slug
* @returns Object with customCss content and loading state
*/
export function useGalleryCustomCss(slug: string) {
const [customCss, setCustomCss] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!slug) {
setLoading(false);
return;
}
const loadCustomCss = async () => {
try {
setLoading(true);
setError(null);
const css = await cssTemplatesService.getGalleryCss(slug);
setCustomCss(css);
} catch (err) {
console.error('Failed to load custom CSS:', err);
setError('Failed to load custom styles');
} finally {
setLoading(false);
}
};
loadCustomCss();
}, [slug]);
// Inject CSS into document
useEffect(() => {
if (!customCss) return;
// Remove any existing custom CSS
const existingStyle = document.getElementById('gallery-custom-css');
if (existingStyle) {
existingStyle.remove();
}
// Create and inject new style element
const styleElement = document.createElement('style');
styleElement.id = 'gallery-custom-css';
styleElement.textContent = customCss;
document.head.appendChild(styleElement);
// Cleanup on unmount or when CSS changes
return () => {
const existing = document.getElementById('gallery-custom-css');
if (existing) {
existing.remove();
}
};
}, [customCss]);
return { customCss, loading, error };
}
export default useGalleryCustomCss;