Fix language setting not being saved to database on admin settings page

- Added default_language field to general settings state in SettingsPage
- Replaced LanguageSelector component with simple select dropdown on settings page
- Fixed public settings endpoint to read general_default_language from database
- Language setting now properly saved when clicking Save Settings button
- Setting is correctly used by gallery login page and legal pages

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
This commit is contained in:
2025-07-08 09:49:45 +02:00
co-authored by Claude
parent cfa0b0da69
commit 2012b0bab9
91 changed files with 6183 additions and 577 deletions
@@ -0,0 +1,107 @@
import React, { useState, useEffect } from 'react';
import { getAuthToken } from '../../config/api';
interface AuthenticatedImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
src: string;
fallbackSrc?: string;
useWatermark?: boolean;
}
export const AuthenticatedImage: React.FC<AuthenticatedImageProps> = ({
src,
fallbackSrc,
alt,
useWatermark = false,
...props
}) => {
const [imageSrc, setImageSrc] = useState<string>('');
const [error, setError] = useState(false);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
let objectUrl: string | null = null;
const token = getAuthToken();
if (!src) {
setImageSrc(fallbackSrc || '');
setIsLoading(false);
return;
}
if (!token) {
console.warn('No auth token found for image:', src);
setImageSrc(fallbackSrc || '');
setIsLoading(false);
return;
}
setIsLoading(true);
setError(false);
// Create a new URL with auth header
const fetchImage = async () => {
try {
// If watermark is requested and this is a gallery photo, use the protected images endpoint
let imageUrl = src;
if (useWatermark && src.includes('/photos/')) {
// Extract gallery slug and photo ID from the URL
// URL format: /photos/events/active/{slug}/photos/{photoId}.jpg
const match = src.match(/\/photos\/events\/active\/([^\/]+)\/photos\/(\d+)\./);
if (match) {
const [, slug, photoId] = match;
imageUrl = `/api/images/${slug}/photo/${photoId}/view`;
}
}
console.log('Fetching authenticated image:', imageUrl);
const response = await fetch(imageUrl, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`);
}
const blob = await response.blob();
objectUrl = URL.createObjectURL(blob);
setImageSrc(objectUrl);
setIsLoading(false);
} catch (err) {
console.error('Failed to load image:', src, err);
setError(true);
setImageSrc(fallbackSrc || '');
setIsLoading(false);
}
};
fetchImage();
// Cleanup function
return () => {
if (objectUrl) {
URL.revokeObjectURL(objectUrl);
}
};
}, [src, fallbackSrc, useWatermark]);
if (isLoading) {
return (
<div className={props.className} style={{ backgroundColor: '#f3f4f6', ...props.style }}>
{/* Show a placeholder while loading */}
</div>
);
}
if (error && fallbackSrc) {
return <img src={fallbackSrc} alt={alt} {...props} />;
}
if (!imageSrc) {
return null;
}
return <img src={imageSrc} alt={alt} {...props} />;
};