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
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
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;
|
||||
Reference in New Issue
Block a user