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
+143 -8
View File
@@ -7,6 +7,7 @@ const bcrypt = require('bcrypt');
const crypto = require('crypto');
const fs = require('fs').promises;
const path = require('path');
const { archiveEvent } = require('../services/archiveService');
// Create new event
router.post('/', adminAuth, [
@@ -360,6 +361,67 @@ router.post('/:id/toggle-status', adminAuth, async (req, res) => {
}
});
// Reset event password
router.post('/:id/reset-password', adminAuth, async (req, res) => {
try {
const { id } = req.params;
const { sendEmail = true } = req.body;
const event = await db('events').where('id', id).first();
if (!event) {
return res.status(404).json({ error: 'Event not found' });
}
if (event.is_archived) {
return res.status(400).json({ error: 'Cannot reset password for archived event' });
}
// Generate new password
const { generatePassword } = require('../utils/passwordGenerator');
const newPassword = generatePassword();
const passwordHash = await bcrypt.hash(newPassword, 10);
// Update event with new password
await db('events')
.where('id', id)
.update({
password_hash: passwordHash,
updated_at: new Date()
});
// Log activity
await logActivity('password_reset',
{ eventName: event.event_name, emailSent: sendEmail },
id,
{ type: 'admin', id: req.admin.id, name: req.admin.username }
);
// Queue email notification if requested
if (sendEmail) {
await db('email_queue').insert({
event_id: id,
recipient_email: event.host_email,
email_type: 'password_reset',
email_data: JSON.stringify({
event_name: event.event_name,
share_link: event.share_link,
new_password: newPassword,
reset_by: req.admin.username
})
});
}
res.json({
message: 'Password reset successfully',
newPassword: newPassword,
emailSent: sendEmail
});
} catch (error) {
console.error('Error resetting password:', error);
res.status(500).json({ error: 'Failed to reset password' });
}
});
// Archive event
router.post('/:id/archive', adminAuth, async (req, res) => {
try {
@@ -374,14 +436,8 @@ router.post('/:id/archive', adminAuth, async (req, res) => {
return res.status(400).json({ error: 'Event is already archived' });
}
await db('events')
.where('id', id)
.update({
is_archived: true,
is_active: false,
archived_at: new Date(),
updated_at: new Date()
});
// Use the archive service to create ZIP archive
await archiveEvent(event);
// Log activity
await logActivity('event_archived',
@@ -397,4 +453,83 @@ router.post('/:id/archive', adminAuth, async (req, res) => {
}
});
// Bulk archive events
router.post('/bulk-archive', adminAuth, [
body('eventIds').isArray().withMessage('eventIds must be an array'),
body('eventIds.*').isInt().withMessage('Each eventId must be an integer')
], async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { eventIds } = req.body;
if (eventIds.length === 0) {
return res.status(400).json({ error: 'No events selected for archiving' });
}
// Get all events to archive
const events = await db('events')
.whereIn('id', eventIds)
.where('is_archived', false);
if (events.length === 0) {
return res.status(400).json({ error: 'No valid events found to archive' });
}
const results = {
successful: [],
failed: []
};
// Process each event
for (const event of events) {
try {
// Use the archive service to create ZIP archive
await archiveEvent(event);
// Log activity
await logActivity('event_archived',
{ eventName: event.event_name, bulkOperation: true },
event.id,
{ type: 'admin', id: req.admin.id, name: req.admin.username }
);
results.successful.push({
id: event.id,
name: event.event_name
});
} catch (error) {
console.error(`Failed to archive event ${event.id}:`, error);
results.failed.push({
id: event.id,
name: event.event_name,
error: error.message
});
}
}
// Log bulk archive activity
await logActivity('bulk_archive_completed',
{
totalEvents: eventIds.length,
successfulCount: results.successful.length,
failedCount: results.failed.length
},
null,
{ type: 'admin', id: req.admin.id, name: req.admin.username }
);
res.json({
message: `Bulk archive completed: ${results.successful.length} succeeded, ${results.failed.length} failed`,
results
});
} catch (error) {
console.error('Error in bulk archive:', error);
res.status(500).json({ error: 'Failed to perform bulk archive' });
}
});
module.exports = router;