Compare commits

...

2 Commits

Author SHA1 Message Date
Gitea Actions Bot 591a119ab3 chore: bump version to 1.0.93 (backend + frontend)
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2025-07-24 13:24:16 +00:00
paul c7cb96aaab fix: resolve multiple feedback management issues
Mirror to GitHub / mirror (push) Successful in 44s
Test and Lint / backend-test (push) Successful in 1m22s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m1s
Version and Release / version-bump (push) Successful in 40s
Version and Release / trigger-drone (push) Successful in 3s
- Hide "Manage Feedback" button when feedback is disabled for an event
- Fix 500 error on feedback API endpoint by adding null-safe operators
- Fix TypeError on analytics page by calculating average_rating in backend
- Fix password validation for event creation by properly awaiting async validation
- Add proper null checks and fallbacks for feedback statistics

These fixes ensure:
- Date passwords like "19.07.2025" work with simple password complexity settings
- Feedback management page loads without errors
- Analytics display correctly even with no feedback data

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-24 15:19:06 +02:00
9 changed files with 50 additions and 20 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "picpeak-backend", "name": "picpeak-backend",
"version": "1.0.84", "version": "1.0.93",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "picpeak-backend", "name": "picpeak-backend",
"version": "1.0.84", "version": "1.0.93",
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.850.0", "@aws-sdk/client-s3": "^3.850.0",
"@aws-sdk/lib-storage": "^3.850.0", "@aws-sdk/lib-storage": "^3.850.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "picpeak-backend", "name": "picpeak-backend",
"version": "1.0.84", "version": "1.0.93",
"description": "Backend for PicPeak event photo sharing platform", "description": "Backend for PicPeak event photo sharing platform",
"main": "server.js", "main": "server.js",
"scripts": { "scripts": {
+1 -1
View File
@@ -42,7 +42,7 @@ router.post('/', adminAuth, [
} = req.body; } = req.body;
// Validate password strength for gallery // Validate password strength for gallery
const passwordValidation = validatePasswordInContext(password, 'gallery', { const passwordValidation = await validatePasswordInContext(password, 'gallery', {
eventName: event_name eventName: event_name
}); });
+1 -1
View File
@@ -53,7 +53,7 @@ router.post('/', adminAuth, [
} = req.body; } = req.body;
// Validate password strength // Validate password strength
const passwordValidation = validatePasswordInContext(password, 'gallery', { const passwordValidation = await validatePasswordInContext(password, 'gallery', {
eventName: event_name eventName: event_name
}); });
+31 -3
View File
@@ -123,8 +123,8 @@ router.get('/events/:eventId/feedback',
pagination: { pagination: {
page: parseInt(page), page: parseInt(page),
limit: parseInt(limit), limit: parseInt(limit),
total: totalCount.count || 0, total: totalCount?.count || 0,
pages: Math.ceil((totalCount.count || 0) / limit) pages: Math.ceil((totalCount?.count || 0) / limit)
} }
}); });
} catch (error) { } catch (error) {
@@ -182,7 +182,35 @@ router.get('/events/:eventId/feedback-analytics',
const { eventId } = req.params; const { eventId } = req.params;
// Get summary statistics // Get summary statistics
const summary = await feedbackService.getEventFeedbackSummary(eventId); const summaryData = await feedbackService.getEventFeedbackSummary(eventId);
// Calculate average rating and other summary stats
const avgRatingResult = await db('photo_feedback')
.where('event_id', eventId)
.where('feedback_type', 'rating')
.avg('rating as average_rating')
.first();
const pendingModeration = await db('photo_feedback')
.where('event_id', eventId)
.where('feedback_type', 'comment')
.where('is_approved', false)
.where('is_hidden', false)
.count('* as count')
.first();
const summary = {
average_rating: parseFloat(avgRatingResult?.average_rating || 0),
total_ratings: summaryData.stats?.total_ratings || 0,
total_likes: summaryData.stats?.total_likes || 0,
total_comments: summaryData.stats?.total_comments || 0,
total_favorites: summaryData.stats?.total_favorites || 0,
pending_moderation: pendingModeration?.count || 0,
total_feedback: (summaryData.stats?.total_ratings || 0) +
(summaryData.stats?.total_likes || 0) +
(summaryData.stats?.total_comments || 0) +
(summaryData.stats?.total_favorites || 0)
};
// Get top-rated photos // Get top-rated photos
const topRated = await db('photos') const topRated = await db('photos')
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "picpeak-frontend", "name": "picpeak-frontend",
"version": "1.0.92", "version": "1.0.93",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "picpeak-frontend", "name": "picpeak-frontend",
"version": "1.0.92", "version": "1.0.93",
"dependencies": { "dependencies": {
"@tanstack/react-query": "^5.0.0", "@tanstack/react-query": "^5.0.0",
"@tiptap/extension-character-count": "^2.26.1", "@tiptap/extension-character-count": "^2.26.1",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "picpeak-frontend", "name": "picpeak-frontend",
"private": true, "private": true,
"version": "1.0.92", "version": "1.0.93",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
+10 -8
View File
@@ -349,14 +349,16 @@ export const EventDetailsPage: React.FC = () => {
{t('common.edit')} {t('common.edit')}
</Button> </Button>
)} )}
<Button {feedbackSettings?.feedback_enabled && (
variant="outline" <Button
size="sm" variant="outline"
leftIcon={<MessageSquare className="w-4 h-4" />} size="sm"
onClick={() => navigate(`/admin/events/${id}/feedback`)} leftIcon={<MessageSquare className="w-4 h-4" />}
> onClick={() => navigate(`/admin/events/${id}/feedback`)}
{t('feedback.manage', 'Manage Feedback')} >
</Button> {t('feedback.manage', 'Manage Feedback')}
</Button>
)}
</> </>
)} )}
{event.share_link && ( {event.share_link && (
@@ -392,7 +392,7 @@ export const EventFeedbackPage: React.FC = () => {
<div className="flex items-center gap-3 mb-2"> <div className="flex items-center gap-3 mb-2">
<Star className="w-8 h-8 text-yellow-500" /> <Star className="w-8 h-8 text-yellow-500" />
<div> <div>
<p className="text-2xl font-bold">{analytics.summary.average_rating.toFixed(1)}</p> <p className="text-2xl font-bold">{(analytics.summary.average_rating || 0).toFixed(1)}</p>
<p className="text-sm text-neutral-600">{t('feedback.avgRating', 'Average Rating')}</p> <p className="text-sm text-neutral-600">{t('feedback.avgRating', 'Average Rating')}</p>
</div> </div>
</div> </div>