d594d00227
- Fixed theme not being reflected on gallery and admin login pages - Created GlobalThemeProvider to apply themes globally - Updated gallery and admin login pages to use dynamic CSS variables - Added complete translations for all admin sections in English and German: - Notifications management - Event view and creation - Photo upload functionality - Category management - Archive page view - Analytics dashboard - Branding and theme settings - System settings - CMS page management - Email configuration - Fixed admin photo management display issues - Fixed photo upload category assignment - Added password reset functionality for galleries - Improved error handling and user feedback 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
93 lines
2.4 KiB
TypeScript
93 lines
2.4 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { api } from '../../config/api';
|
|
|
|
interface AdminAuthenticatedImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
|
src: string;
|
|
fallback?: React.ReactNode;
|
|
}
|
|
|
|
export const AdminAuthenticatedImage: React.FC<AdminAuthenticatedImageProps> = ({
|
|
src,
|
|
fallback,
|
|
alt,
|
|
...props
|
|
}) => {
|
|
const [imageSrc, setImageSrc] = useState<string | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState(false);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
const loadImage = async () => {
|
|
try {
|
|
setLoading(true);
|
|
setError(false);
|
|
|
|
// Make authenticated request to get the image
|
|
const response = await api.get(src, {
|
|
responseType: 'blob',
|
|
});
|
|
|
|
if (!cancelled) {
|
|
// Create object URL from blob
|
|
const imageUrl = URL.createObjectURL(response.data);
|
|
setImageSrc(imageUrl);
|
|
setLoading(false);
|
|
}
|
|
} catch (err: any) {
|
|
console.error('Failed to load image:', src, err);
|
|
// Log more details about the error
|
|
if (err.response) {
|
|
console.error('Response status:', err.response.status);
|
|
console.error('Response headers:', err.response.headers);
|
|
if (err.response.data instanceof Blob) {
|
|
// Try to read error message from blob
|
|
try {
|
|
const text = await err.response.data.text();
|
|
console.error('Response data:', text);
|
|
} catch (e) {
|
|
console.error('Could not read blob data');
|
|
}
|
|
} else {
|
|
console.error('Response data:', err.response.data);
|
|
}
|
|
}
|
|
if (!cancelled) {
|
|
setError(true);
|
|
setLoading(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (src) {
|
|
loadImage();
|
|
}
|
|
|
|
// Cleanup function
|
|
return () => {
|
|
cancelled = true;
|
|
if (imageSrc) {
|
|
URL.revokeObjectURL(imageSrc);
|
|
}
|
|
};
|
|
}, [src]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="w-full h-full bg-neutral-200 animate-pulse" />
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return fallback ? (
|
|
<>{fallback}</>
|
|
) : (
|
|
<div className="w-full h-full bg-neutral-100 flex items-center justify-center text-neutral-400">
|
|
<span className="text-xs">Failed to load</span>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return <img src={imageSrc || ''} alt={alt} {...props} />;
|
|
}; |