Fix brand theme application and add comprehensive translations

- 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>
This commit is contained in:
2025-07-08 17:07:40 +02:00
parent 2012b0bab9
commit d594d00227
79 changed files with 4570 additions and 329 deletions
+55 -14
View File
@@ -1,15 +1,17 @@
import React, { useState } from 'react';
import { Navigate } from 'react-router-dom';
import React, { useState, useEffect } from 'react';
import { Navigate, useSearchParams } from 'react-router-dom';
import { Lock, Mail, Eye, EyeOff, AlertCircle } from 'lucide-react';
import { toast } from 'react-toastify';
import { useQuery } from '@tanstack/react-query';
import { Button, Input, Card } from '../../components/common';
import { Button, Input, Card, ReCaptcha } from '../../components/common';
import { useAdminAuth } from '../../contexts';
import { authService } from '../../services/auth.service';
import { getAuthToken } from '../../config/api';
import { getAuthToken, api } from '../../config/api';
export const AdminLoginPage: React.FC = () => {
const { isAuthenticated, login } = useAdminAuth();
const [searchParams] = useSearchParams();
const [formData, setFormData] = useState({
email: '',
@@ -19,6 +21,24 @@ export const AdminLoginPage: React.FC = () => {
const [isLoading, setIsLoading] = useState(false);
const [errors, setErrors] = useState<Record<string, string>>({});
const [loginSuccess, setLoginSuccess] = useState(false);
const [recaptchaToken, setRecaptchaToken] = useState<string | null>(null);
// Fetch branding settings
const { data: settingsData } = useQuery({
queryKey: ['admin-login-settings'],
queryFn: async () => {
const response = await api.get('/api/public/settings');
return response.data;
},
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
});
// Check for session expired message
useEffect(() => {
if (searchParams.get('session') === 'expired') {
toast.info('Your session has expired. Please log in again.');
}
}, [searchParams]);
// Redirect if already authenticated or login successful
if (isAuthenticated || loginSuccess) {
@@ -55,7 +75,10 @@ export const AdminLoginPage: React.FC = () => {
setErrors({});
try {
const response = await authService.adminLogin(formData);
const response = await authService.adminLogin({
...formData,
recaptchaToken
});
login(response.token, response.user);
toast.success('Login successful!');
setLoginSuccess(true);
@@ -93,15 +116,23 @@ export const AdminLoginPage: React.FC = () => {
};
return (
<div className="min-h-screen bg-gradient-to-br from-primary-50 to-neutral-100 flex items-center justify-center p-4">
<div className="min-h-screen flex items-center justify-center p-4" style={{ backgroundColor: 'var(--color-background, #fafafa)' }}>
<div className="w-full max-w-md">
{/* Logo/Header */}
<div className="text-center mb-8">
<div className="inline-flex items-center justify-center w-16 h-16 bg-primary-600 rounded-full mb-4">
<Lock className="w-8 h-8 text-white" />
</div>
<h1 className="text-3xl font-bold text-neutral-900">Admin Login</h1>
<p className="text-neutral-600 mt-2">Sign in to manage your photo galleries</p>
{settingsData?.branding_logo_url ? (
<img
src={`${import.meta.env.VITE_API_URL || 'http://localhost:3001'}${settingsData.branding_logo_url}`}
alt={settingsData.branding_company_name || 'Company Logo'}
className="h-16 w-auto object-contain mx-auto mb-4"
/>
) : (
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full mb-4" style={{ backgroundColor: 'var(--color-primary, #5C8762)' }}>
<Lock className="w-8 h-8 text-white" />
</div>
)}
<h1 className="text-3xl font-bold" style={{ color: 'var(--color-text, #171717)' }}>Admin Login</h1>
<p className="mt-2" style={{ color: 'var(--color-text, #171717)', opacity: 0.7 }}>Sign in to manage your photo galleries</p>
</div>
{/* Login Form */}
@@ -178,6 +209,12 @@ export const AdminLoginPage: React.FC = () => {
</a>
</div>
{/* reCAPTCHA */}
<ReCaptcha
onChange={setRecaptchaToken}
onExpired={() => setRecaptchaToken(null)}
/>
{/* Submit Button */}
<Button
type="submit"
@@ -192,10 +229,14 @@ export const AdminLoginPage: React.FC = () => {
</Card>
{/* Footer */}
<p className="text-center text-sm text-neutral-600 mt-8">
<p className="text-center text-sm mt-8" style={{ color: 'var(--color-text, #171717)', opacity: 0.7 }}>
Need help? Contact{' '}
<a href="mailto:support@example.com" className="text-primary-600 hover:text-primary-700">
support@example.com
<a
href={`mailto:${settingsData?.branding_support_email || 'support@example.com'}`}
className="hover:underline"
style={{ color: 'var(--color-primary, #5C8762)' }}
>
{settingsData?.branding_support_email || 'support@example.com'}
</a>
</p>