Fix translation issues in event creation and detail pages

- Add missing translation keys for event creation form
- Replace all hardcoded strings with translation keys in CreateEventPage
- Add validation error message translations
- Add event types and color theme translations
- Add errors and validation sections to translation files
- Update German translations with corresponding keys

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-09 09:25:15 +02:00
parent 8231f2b60d
commit 0a203d16cf
3 changed files with 153 additions and 58 deletions
+58 -1
View File
@@ -28,7 +28,8 @@
"refresh": "Aktualisieren", "refresh": "Aktualisieren",
"preview": "Vorschau", "preview": "Vorschau",
"processing": "Wird verarbeitet...", "processing": "Wird verarbeitet...",
"upload": "Hochladen" "upload": "Hochladen",
"days": "Tage"
}, },
"upload": { "upload": {
"photoCategory": "Fotokategorie", "photoCategory": "Fotokategorie",
@@ -245,6 +246,30 @@
"days365": "1 Jahr", "days365": "1 Jahr",
"createNewEvent": "Neue Veranstaltung erstellen", "createNewEvent": "Neue Veranstaltung erstellen",
"setupNewGallery": "Richten Sie eine neue Fotogalerie für Ihre Veranstaltung ein", "setupNewGallery": "Richten Sie eine neue Fotogalerie für Ihre Veranstaltung ein",
"createNewEventSubtitle": "Richten Sie eine neue Fotogalerie für Ihre Veranstaltung ein",
"eventNamePlaceholder": "z.B. Max & Maria's Hochzeit",
"welcomeMessageOptional": "Willkommensnachricht (Optional)",
"welcomeMessagePlaceholder": "Willkommen zu unserem besonderen Tag! Laden Sie diese Erinnerungen gerne herunter und teilen Sie sie...",
"hostEmailPlaceholder": "gastgeber@beispiel.de",
"adminEmailPlaceholder": "admin@beispiel.de",
"securityAndAccess": "Sicherheit & Zugriff",
"enterPassword": "Passwort eingeben",
"confirmPasswordPlaceholder": "Passwort bestätigen",
"galleryExpiresOn": "Galerie läuft ab am {{date}}",
"guestsWillReceiveWarning": "Gäste erhalten 7 Tage vor Ablauf eine Warn-E-Mail.",
"types": {
"wedding": "Hochzeit",
"birthday": "Geburtstag",
"corporate": "Geschäftlich",
"other": "Andere"
},
"themes": {
"default": "Standard",
"oceanBlue": "Ozeanblau",
"royalPurple": "Königliches Lila",
"roseGold": "Roségold",
"sunsetAmber": "Sonnenuntergang Bernstein"
},
"adminNotificationEmail": "Admin-Benachrichtigungs-E-Mail", "adminNotificationEmail": "Admin-Benachrichtigungs-E-Mail",
"inactive": "Inaktiv", "inactive": "Inaktiv",
"expired": "Abgelaufen", "expired": "Abgelaufen",
@@ -602,6 +627,38 @@
"datenschutz": "Datenschutzerklärung", "datenschutz": "Datenschutzerklärung",
"pageUpdated": "Seite erfolgreich aktualisiert" "pageUpdated": "Seite erfolgreich aktualisiert"
}, },
"errors": {
"notFound": "Nicht gefunden",
"galleryNotFound": "Galerie nicht gefunden",
"galleryNotFoundMessage": "Diese Galerie existiert nicht oder wurde entfernt.",
"galleryArchived": "Galerie archiviert",
"galleryArchivedMessage": "Diese Galerie wurde archiviert und ist nicht mehr zugänglich. Bitte kontaktieren Sie den Veranstalter, wenn Sie Zugriff auf diese Fotos benötigen.",
"unauthorized": "Nicht autorisiert",
"forbidden": "Verboten",
"serverError": "Serverfehler",
"somethingWentWrong": "Etwas ist schiefgelaufen",
"tryAgainLater": "Bitte versuchen Sie es später erneut",
"refreshPage": "Seite aktualisieren",
"oopsSomethingWentWrong": "Hoppla! Etwas ist schiefgelaufen",
"unexpectedError": "Ein unerwarteter Fehler ist aufgetreten. Keine Sorge, Ihre Daten sind sicher.",
"goToHomepage": "Zur Startseite",
"errorDetails": "Fehlerdetails",
"requiredFields": "Bitte füllen Sie alle Pflichtfelder aus",
"enterTestEmail": "Bitte geben Sie eine Test-E-Mail-Adresse ein",
"failedToCreateEvent": "Veranstaltung konnte nicht erstellt werden",
"networkError": "Netzwerkfehler. Bitte überprüfen Sie Ihre Verbindung und versuchen Sie es erneut.",
"sessionExpired": "Sitzung abgelaufen. Bitte melden Sie sich erneut an."
},
"validation": {
"eventNameRequired": "Veranstaltungsname ist erforderlich",
"hostEmailRequired": "Gastgeber-E-Mail ist erforderlich",
"adminEmailRequired": "Admin-E-Mail ist erforderlich",
"invalidEmailFormat": "Ungültiges E-Mail-Format",
"passwordRequired": "Passwort ist erforderlich",
"passwordMinLength": "Passwort muss mindestens 6 Zeichen lang sein",
"passwordsDoNotMatch": "Passwörter stimmen nicht überein",
"expirationRange": "Ablauf muss zwischen 1 und 365 Tagen liegen"
},
"maintenance": { "maintenance": {
"title": "Systemwartung", "title": "Systemwartung",
"message": "Wir führen derzeit geplante Wartungsarbeiten durch, um unseren Service zu verbessern. Wir sind in Kürze wieder online.", "message": "Wir führen derzeit geplante Wartungsarbeiten durch, um unseren Service zu verbessern. Wir sind in Kürze wieder online.",
+40 -2
View File
@@ -28,7 +28,8 @@
"refresh": "Refresh", "refresh": "Refresh",
"preview": "Preview", "preview": "Preview",
"processing": "Processing...", "processing": "Processing...",
"upload": "Upload" "upload": "Upload",
"days": "days"
}, },
"upload": { "upload": {
"photoCategory": "Photo Category", "photoCategory": "Photo Category",
@@ -165,6 +166,30 @@
"createEvent": "Create Event", "createEvent": "Create Event",
"createNewEvent": "Create New Event", "createNewEvent": "Create New Event",
"setupNewGallery": "Set up a new photo gallery for your event", "setupNewGallery": "Set up a new photo gallery for your event",
"createNewEventSubtitle": "Set up a new photo gallery for your event",
"eventNamePlaceholder": "e.g., John & Jane's Wedding",
"welcomeMessageOptional": "Welcome Message (Optional)",
"welcomeMessagePlaceholder": "Welcome to our special day! Feel free to download and share these memories...",
"hostEmailPlaceholder": "host@example.com",
"adminEmailPlaceholder": "admin@example.com",
"securityAndAccess": "Security & Access",
"enterPassword": "Enter password",
"confirmPasswordPlaceholder": "Confirm password",
"galleryExpiresOn": "Gallery will expire on {{date}}",
"guestsWillReceiveWarning": "Guests will receive a warning email 7 days before expiration.",
"types": {
"wedding": "Wedding",
"birthday": "Birthday",
"corporate": "Corporate",
"other": "Other"
},
"themes": {
"default": "Default",
"oceanBlue": "Ocean Blue",
"royalPurple": "Royal Purple",
"roseGold": "Rose Gold",
"sunsetAmber": "Sunset Amber"
},
"eventDetails": "Event Details", "eventDetails": "Event Details",
"eventName": "Event Name", "eventName": "Event Name",
"eventType": "Event Type", "eventType": "Event Type",
@@ -548,7 +573,20 @@
"goToHomepage": "Go to Homepage", "goToHomepage": "Go to Homepage",
"errorDetails": "Error Details", "errorDetails": "Error Details",
"requiredFields": "Please fill in all required fields", "requiredFields": "Please fill in all required fields",
"enterTestEmail": "Please enter a test email address" "enterTestEmail": "Please enter a test email address",
"failedToCreateEvent": "Failed to create event",
"networkError": "Network error. Please check your connection and try again.",
"sessionExpired": "Session expired. Please login again."
},
"validation": {
"eventNameRequired": "Event name is required",
"hostEmailRequired": "Host email is required",
"adminEmailRequired": "Admin email is required",
"invalidEmailFormat": "Invalid email format",
"passwordRequired": "Password is required",
"passwordMinLength": "Password must be at least 6 characters",
"passwordsDoNotMatch": "Passwords do not match",
"expirationRange": "Expiration must be between 1 and 365 days"
}, },
"legal": { "legal": {
"impressum": "Legal Notice", "impressum": "Legal Notice",
+55 -55
View File
@@ -34,16 +34,16 @@ interface FormData {
} }
const EVENT_TYPES = [ const EVENT_TYPES = [
{ value: 'wedding', label: 'Wedding', emoji: '💒' }, { value: 'wedding', labelKey: 'events.types.wedding', emoji: '💒' },
{ value: 'birthday', label: 'Birthday', emoji: '🎂' }, { value: 'birthday', labelKey: 'events.types.birthday', emoji: '🎂' },
{ value: 'corporate', label: 'Corporate', emoji: '🏢' }, { value: 'corporate', labelKey: 'events.types.corporate', emoji: '🏢' },
{ value: 'other', label: 'Other', emoji: '📸' }, { value: 'other', labelKey: 'events.types.other', emoji: '📸' },
]; ];
const COLOR_THEMES = [ const COLOR_THEMES = [
{ {
value: 'default', value: 'default',
label: 'Default (Green)', labelKey: 'events.themes.default',
color: 'bg-primary-600', color: 'bg-primary-600',
theme: { theme: {
primaryColor: '#5C8762', primaryColor: '#5C8762',
@@ -55,7 +55,7 @@ const COLOR_THEMES = [
}, },
{ {
value: 'blue', value: 'blue',
label: 'Ocean Blue', labelKey: 'events.themes.oceanBlue',
color: 'bg-blue-600', color: 'bg-blue-600',
theme: { theme: {
primaryColor: '#2563eb', primaryColor: '#2563eb',
@@ -67,7 +67,7 @@ const COLOR_THEMES = [
}, },
{ {
value: 'purple', value: 'purple',
label: 'Royal Purple', labelKey: 'events.themes.royalPurple',
color: 'bg-purple-600', color: 'bg-purple-600',
theme: { theme: {
primaryColor: '#9333ea', primaryColor: '#9333ea',
@@ -79,7 +79,7 @@ const COLOR_THEMES = [
}, },
{ {
value: 'rose', value: 'rose',
label: 'Rose Gold', labelKey: 'events.themes.roseGold',
color: 'bg-rose-600', color: 'bg-rose-600',
theme: { theme: {
primaryColor: '#e11d48', primaryColor: '#e11d48',
@@ -91,7 +91,7 @@ const COLOR_THEMES = [
}, },
{ {
value: 'amber', value: 'amber',
label: 'Sunset Amber', labelKey: 'events.themes.sunsetAmber',
color: 'bg-amber-600', color: 'bg-amber-600',
theme: { theme: {
primaryColor: '#d97706', primaryColor: '#d97706',
@@ -142,7 +142,7 @@ export const CreateEventPage: React.FC = () => {
mutationFn: eventsService.createEvent, mutationFn: eventsService.createEvent,
onSuccess: (data) => { onSuccess: (data) => {
if (isMountedRef.current) { if (isMountedRef.current) {
toast.success('Event created successfully!'); toast.success(t('toast.eventCreated'));
// Add a small delay to ensure navigation works properly // Add a small delay to ensure navigation works properly
setTimeout(() => { setTimeout(() => {
if (isMountedRef.current && data?.id) { if (isMountedRef.current && data?.id) {
@@ -157,7 +157,7 @@ export const CreateEventPage: React.FC = () => {
if (!isMountedRef.current) return; if (!isMountedRef.current) return;
if (error.code === 'ERR_NETWORK' || error.code === 'ERR_CONNECTION_RESET') { if (error.code === 'ERR_NETWORK' || error.code === 'ERR_CONNECTION_RESET') {
toast.error('Network error. Please check your connection and try again.'); toast.error(t('errors.networkError'));
} else if (error.response?.data?.errors) { } else if (error.response?.data?.errors) {
const newErrors: Record<string, string> = {}; const newErrors: Record<string, string> = {};
error.response.data.errors.forEach((err: any) => { error.response.data.errors.forEach((err: any) => {
@@ -165,10 +165,10 @@ export const CreateEventPage: React.FC = () => {
}); });
setErrors(newErrors); setErrors(newErrors);
} else if (error.response?.status === 401) { } else if (error.response?.status === 401) {
toast.error('Session expired. Please login again.'); toast.error(t('errors.sessionExpired'));
navigate('/admin/login'); navigate('/admin/login');
} else { } else {
toast.error(error.response?.data?.error || 'Failed to create event'); toast.error(error.response?.data?.error || t('errors.failedToCreateEvent'));
} }
}, },
}); });
@@ -177,33 +177,33 @@ export const CreateEventPage: React.FC = () => {
const newErrors: Partial<Record<keyof FormData, string>> = {}; const newErrors: Partial<Record<keyof FormData, string>> = {};
if (!formData.event_name.trim()) { if (!formData.event_name.trim()) {
newErrors.event_name = 'Event name is required'; newErrors.event_name = t('validation.eventNameRequired');
} }
if (!formData.host_email) { if (!formData.host_email) {
newErrors.host_email = 'Host email is required'; newErrors.host_email = t('validation.hostEmailRequired');
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.host_email)) { } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.host_email)) {
newErrors.host_email = 'Invalid email format'; newErrors.host_email = t('validation.invalidEmailFormat');
} }
if (!formData.admin_email) { if (!formData.admin_email) {
newErrors.admin_email = 'Admin email is required'; newErrors.admin_email = t('validation.adminEmailRequired');
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.admin_email)) { } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.admin_email)) {
newErrors.admin_email = 'Invalid email format'; newErrors.admin_email = t('validation.invalidEmailFormat');
} }
if (!formData.password) { if (!formData.password) {
newErrors.password = 'Password is required'; newErrors.password = t('validation.passwordRequired');
} else if (formData.password.length < 6) { } else if (formData.password.length < 6) {
newErrors.password = 'Password must be at least 6 characters'; newErrors.password = t('validation.passwordMinLength');
} }
if (formData.password !== formData.confirm_password) { if (formData.password !== formData.confirm_password) {
newErrors.confirm_password = 'Passwords do not match'; newErrors.confirm_password = t('validation.passwordsDoNotMatch');
} }
if (formData.expires_in_days < 1 || formData.expires_in_days > 365) { if (formData.expires_in_days < 1 || formData.expires_in_days > 365) {
newErrors.expires_in_days = 'Expiration must be between 1 and 365 days'; newErrors.expires_in_days = t('validation.expirationRange');
} }
setErrors(newErrors); setErrors(newErrors);
@@ -257,23 +257,23 @@ export const CreateEventPage: React.FC = () => {
onClick={() => navigate('/admin/events')} onClick={() => navigate('/admin/events')}
className="mb-4" className="mb-4"
> >
Back to Events {t('events.backToEvents')}
</Button> </Button>
<h1 className="text-2xl font-bold text-neutral-900">Create New Event</h1> <h1 className="text-2xl font-bold text-neutral-900">{t('events.createNewEvent')}</h1>
<p className="text-neutral-600 mt-1">Set up a new photo gallery for your event</p> <p className="text-neutral-600 mt-1">{t('events.createNewEventSubtitle')}</p>
</div> </div>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
{/* Event Details */} {/* Event Details */}
<Card padding="md" className="mb-6"> <Card padding="md" className="mb-6">
<h2 className="text-lg font-semibold text-neutral-900 mb-4">Event Details</h2> <h2 className="text-lg font-semibold text-neutral-900 mb-4">{t('events.eventDetails')}</h2>
<div className="space-y-4"> <div className="space-y-4">
{/* Event Type */} {/* Event Type */}
<div> <div>
<label className="block text-sm font-medium text-neutral-700 mb-1"> <label className="block text-sm font-medium text-neutral-700 mb-1">
Event Type {t('events.eventType')}
</label> </label>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3"> <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3">
{EVENT_TYPES.map(type => ( {EVENT_TYPES.map(type => (
@@ -288,7 +288,7 @@ export const CreateEventPage: React.FC = () => {
}`} }`}
> >
<div className="text-2xl mb-1">{type.emoji}</div> <div className="text-2xl mb-1">{type.emoji}</div>
<div className="text-sm font-medium">{type.label}</div> <div className="text-sm font-medium">{t(type.labelKey)}</div>
</button> </button>
))} ))}
</div> </div>
@@ -297,7 +297,7 @@ export const CreateEventPage: React.FC = () => {
{/* Event Name */} {/* Event Name */}
<div> <div>
<label htmlFor="event_name" className="block text-sm font-medium text-neutral-700 mb-1"> <label htmlFor="event_name" className="block text-sm font-medium text-neutral-700 mb-1">
Event Name {t('events.eventName')}
</label> </label>
<Input <Input
id="event_name" id="event_name"
@@ -305,7 +305,7 @@ export const CreateEventPage: React.FC = () => {
value={formData.event_name} value={formData.event_name}
onChange={handleInputChange('event_name')} onChange={handleInputChange('event_name')}
error={errors.event_name} error={errors.event_name}
placeholder="e.g., Smith-Jones Wedding" placeholder={t('events.eventNamePlaceholder')}
leftIcon={<Calendar className="w-5 h-5 text-neutral-400" />} leftIcon={<Calendar className="w-5 h-5 text-neutral-400" />}
/> />
</div> </div>
@@ -313,7 +313,7 @@ export const CreateEventPage: React.FC = () => {
{/* Event Date */} {/* Event Date */}
<div> <div>
<label htmlFor="event_date" className="block text-sm font-medium text-neutral-700 mb-1"> <label htmlFor="event_date" className="block text-sm font-medium text-neutral-700 mb-1">
Event Date {t('events.eventDate')}
</label> </label>
<Input <Input
id="event_date" id="event_date"
@@ -327,13 +327,13 @@ export const CreateEventPage: React.FC = () => {
{/* Welcome Message */} {/* Welcome Message */}
<div> <div>
<label htmlFor="welcome_message" className="block text-sm font-medium text-neutral-700 mb-1"> <label htmlFor="welcome_message" className="block text-sm font-medium text-neutral-700 mb-1">
Welcome Message (Optional) {t('events.welcomeMessageOptional')}
</label> </label>
<textarea <textarea
id="welcome_message" id="welcome_message"
value={formData.welcome_message} value={formData.welcome_message}
onChange={handleInputChange('welcome_message')} onChange={handleInputChange('welcome_message')}
placeholder="A personalized message for your guests..." placeholder={t('events.welcomeMessagePlaceholder')}
className="w-full px-3 py-2 border border-neutral-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500" className="w-full px-3 py-2 border border-neutral-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
rows={3} rows={3}
/> />
@@ -343,13 +343,13 @@ export const CreateEventPage: React.FC = () => {
{/* Contact Information */} {/* Contact Information */}
<Card padding="md" className="mb-6"> <Card padding="md" className="mb-6">
<h2 className="text-lg font-semibold text-neutral-900 mb-4">Contact Information</h2> <h2 className="text-lg font-semibold text-neutral-900 mb-4">{t('events.contactInformation')}</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Host Email */} {/* Host Email */}
<div> <div>
<label htmlFor="host_email" className="block text-sm font-medium text-neutral-700 mb-1"> <label htmlFor="host_email" className="block text-sm font-medium text-neutral-700 mb-1">
Host Email {t('events.hostEmail')}
</label> </label>
<Input <Input
id="host_email" id="host_email"
@@ -357,18 +357,18 @@ export const CreateEventPage: React.FC = () => {
value={formData.host_email} value={formData.host_email}
onChange={handleInputChange('host_email')} onChange={handleInputChange('host_email')}
error={errors.host_email} error={errors.host_email}
placeholder="host@example.com" placeholder={t('events.hostEmailPlaceholder')}
leftIcon={<Mail className="w-5 h-5 text-neutral-400" />} leftIcon={<Mail className="w-5 h-5 text-neutral-400" />}
/> />
<p className="text-xs text-neutral-500 mt-1"> <p className="text-xs text-neutral-500 mt-1">
Will receive gallery creation and expiration notifications {t('events.hostEmailHelp')}
</p> </p>
</div> </div>
{/* Admin Email */} {/* Admin Email */}
<div> <div>
<label htmlFor="admin_email" className="block text-sm font-medium text-neutral-700 mb-1"> <label htmlFor="admin_email" className="block text-sm font-medium text-neutral-700 mb-1">
Admin Notification Email {t('events.adminNotificationEmail')}
</label> </label>
<Input <Input
id="admin_email" id="admin_email"
@@ -376,11 +376,11 @@ export const CreateEventPage: React.FC = () => {
value={formData.admin_email} value={formData.admin_email}
onChange={handleInputChange('admin_email')} onChange={handleInputChange('admin_email')}
error={errors.admin_email} error={errors.admin_email}
placeholder="admin@example.com" placeholder={t('events.adminEmailPlaceholder')}
leftIcon={<Mail className="w-5 h-5 text-neutral-400" />} leftIcon={<Mail className="w-5 h-5 text-neutral-400" />}
/> />
<p className="text-xs text-neutral-500 mt-1"> <p className="text-xs text-neutral-500 mt-1">
Will receive system notifications and archive confirmations {t('events.adminEmailHelp')}
</p> </p>
</div> </div>
</div> </div>
@@ -388,13 +388,13 @@ export const CreateEventPage: React.FC = () => {
{/* Security & Access */} {/* Security & Access */}
<Card padding="md" className="mb-6"> <Card padding="md" className="mb-6">
<h2 className="text-lg font-semibold text-neutral-900 mb-4">Security & Access</h2> <h2 className="text-lg font-semibold text-neutral-900 mb-4">{t('events.securityAndAccess')}</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Password */} {/* Password */}
<div> <div>
<label htmlFor="password" className="block text-sm font-medium text-neutral-700 mb-1"> <label htmlFor="password" className="block text-sm font-medium text-neutral-700 mb-1">
Gallery Password {t('events.galleryPassword')}
</label> </label>
<div className="relative"> <div className="relative">
<Input <Input
@@ -403,7 +403,7 @@ export const CreateEventPage: React.FC = () => {
value={formData.password} value={formData.password}
onChange={handleInputChange('password')} onChange={handleInputChange('password')}
error={errors.password} error={errors.password}
placeholder="Enter password" placeholder={t('events.enterPassword')}
leftIcon={<Lock className="w-5 h-5 text-neutral-400" />} leftIcon={<Lock className="w-5 h-5 text-neutral-400" />}
/> />
</div> </div>
@@ -412,7 +412,7 @@ export const CreateEventPage: React.FC = () => {
{/* Confirm Password */} {/* Confirm Password */}
<div> <div>
<label htmlFor="confirm_password" className="block text-sm font-medium text-neutral-700 mb-1"> <label htmlFor="confirm_password" className="block text-sm font-medium text-neutral-700 mb-1">
Confirm Password {t('events.confirmPassword')}
</label> </label>
<Input <Input
id="confirm_password" id="confirm_password"
@@ -420,7 +420,7 @@ export const CreateEventPage: React.FC = () => {
value={formData.confirm_password} value={formData.confirm_password}
onChange={handleInputChange('confirm_password')} onChange={handleInputChange('confirm_password')}
error={errors.confirm_password} error={errors.confirm_password}
placeholder="Confirm password" placeholder={t('events.confirmPasswordPlaceholder')}
leftIcon={<Lock className="w-5 h-5 text-neutral-400" />} leftIcon={<Lock className="w-5 h-5 text-neutral-400" />}
/> />
</div> </div>
@@ -434,20 +434,20 @@ export const CreateEventPage: React.FC = () => {
onChange={(e) => setShowPassword(e.target.checked)} onChange={(e) => setShowPassword(e.target.checked)}
className="w-4 h-4 text-primary-600 border-neutral-300 rounded focus:ring-primary-500" className="w-4 h-4 text-primary-600 border-neutral-300 rounded focus:ring-primary-500"
/> />
<span className="ml-2 text-sm text-neutral-700">Show passwords</span> <span className="ml-2 text-sm text-neutral-700">{t('events.showPasswords')}</span>
</label> </label>
</div> </div>
</Card> </Card>
{/* Gallery Settings */} {/* Gallery Settings */}
<Card padding="md" className="mb-6"> <Card padding="md" className="mb-6">
<h2 className="text-lg font-semibold text-neutral-900 mb-4">Gallery Settings</h2> <h2 className="text-lg font-semibold text-neutral-900 mb-4">{t('events.gallerySettings')}</h2>
<div className="space-y-4"> <div className="space-y-4">
{/* Color Theme */} {/* Color Theme */}
<div> <div>
<label className="block text-sm font-medium text-neutral-700 mb-1"> <label className="block text-sm font-medium text-neutral-700 mb-1">
Color Theme {t('events.colorTheme')}
</label> </label>
<div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3"> <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3">
{COLOR_THEMES.map(theme => ( {COLOR_THEMES.map(theme => (
@@ -462,7 +462,7 @@ export const CreateEventPage: React.FC = () => {
}`} }`}
> >
<div className={`w-full h-8 ${theme.color} rounded mb-2`} /> <div className={`w-full h-8 ${theme.color} rounded mb-2`} />
<div className="text-xs font-medium">{theme.label}</div> <div className="text-xs font-medium">{t(theme.labelKey)}</div>
</button> </button>
))} ))}
</div> </div>
@@ -471,7 +471,7 @@ export const CreateEventPage: React.FC = () => {
{/* Expiration */} {/* Expiration */}
<div> <div>
<label htmlFor="expires_in_days" className="block text-sm font-medium text-neutral-700 mb-1"> <label htmlFor="expires_in_days" className="block text-sm font-medium text-neutral-700 mb-1">
Gallery Expires In {t('events.galleryExpiresIn')}
</label> </label>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Input <Input
@@ -485,13 +485,13 @@ export const CreateEventPage: React.FC = () => {
className="w-32" className="w-32"
leftIcon={<Clock className="w-5 h-5 text-neutral-400" />} leftIcon={<Clock className="w-5 h-5 text-neutral-400" />}
/> />
<span className="text-sm text-neutral-700">days</span> <span className="text-sm text-neutral-700">{t('common.days')}</span>
</div> </div>
<div className="mt-2 p-3 bg-blue-50 rounded-lg flex items-start gap-2"> <div className="mt-2 p-3 bg-blue-50 rounded-lg flex items-start gap-2">
<Info className="w-5 h-5 text-blue-600 flex-shrink-0" /> <Info className="w-5 h-5 text-blue-600 flex-shrink-0" />
<div className="text-sm text-blue-800"> <div className="text-sm text-blue-800">
<p>Gallery will expire on {format(addDays(new Date(), formData.expires_in_days), 'MMMM d, yyyy')}</p> <p>{t('events.galleryExpiresOn', { date: format(addDays(new Date(), formData.expires_in_days), 'MMMM d, yyyy') })}</p>
<p className="mt-1">Guests will receive a warning email 7 days before expiration.</p> <p className="mt-1">{t('events.guestsWillReceiveWarning')}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -565,14 +565,14 @@ export const CreateEventPage: React.FC = () => {
variant="outline" variant="outline"
onClick={() => navigate('/admin/events')} onClick={() => navigate('/admin/events')}
> >
Cancel {t('common.cancel')}
</Button> </Button>
<Button <Button
type="submit" type="submit"
variant="primary" variant="primary"
isLoading={createMutation.isPending} isLoading={createMutation.isPending}
> >
Create Event {t('events.createEvent')}
</Button> </Button>
</div> </div>
</form> </form>