Enabled profile links for all usernames in the event Participants sidebar.
Users can now click on any participant's username to view their public
profile page at /u/username.
This improves user discoverability and allows participants to learn more
about other dancers before connecting or sending match requests.
Changes:
- Set linkToProfile={true} in ParticipantsSidebar for all UserListItem components
- Usernames now display as clickable links with hover state
Removed "Run matching" buttons from the Recording tab to prevent manual
triggering. The system now only displays matching status information:
- Shows countdown when registration deadline is approaching
- Shows last run time when matching has been completed
- Shows informational message when matching hasn't run yet
This ensures matching is only triggered automatically by the system
or through the admin interface, maintaining better control over the
matching process.
Changes:
- Removed handleRunMatching function and runningMatching state
- Replaced actionable buttons with informational status displays
- Improved date/time formatting for last run timestamp
- Changed "not run yet" status to positive "will be run soon" message
Replaced all confirm() dialogs with reusable ConfirmationModal component
for better UX. Modal dialogs provide clearer context, visual consistency,
and prevent accidental confirmations.
Changes:
- MatchesPage: Reject match request confirmation modal
- DashboardPage: Decline and cancel request confirmation modals
- ContactMessagesPage: Delete message confirmation modal
All modals support loading states during async operations and provide
clear action descriptions with destructive action styling.
Replaced all alert() calls with react-hot-toast notifications for better
user experience. Toast notifications are non-blocking, auto-dismiss, and
provide visual feedback with icons based on message type.
Changes:
- EventChatPage: Match request success/error toasts
- MatchChatPage: Video file selection and WebRTC connection error toasts
- MatchesPage: Match accept/reject action toasts
- RatePartnerPage: Rating submission and validation toasts
- VerifyEmailPage: Email verification sent toast
- ScheduleConfigSection: Schedule save success/error toasts
- MatchingConfigSection: Deadline save success/error toasts
All toast notifications use appropriate types (success, error, warning, info)
for better visual distinction and user feedback.
Implemented consistent navigation across all public-facing pages with a
reusable layout system. Created PublicLayout component that wraps pages
with a header containing the logo and a footer with navigation links.
Changes:
- Created PublicHeader component with logo linking to homepage
- Created PublicFooter component with Product, Account, and Support sections
- Created PublicLayout wrapper component using flex layout
- Updated all public pages to use PublicLayout:
- LoginPage, RegisterPage, ForgotPasswordPage, ResetPasswordPage
- VerifyEmailPage, ContactPage, AboutUsPage, HowItWorksPage
- NotFoundPage
- Fixed gradient background pages to use min-h-full for proper height
- Fixed content pages to avoid min-h-screen conflicts with flex-grow
- Updated About Us content
- Update test count to 351/351 (from 350)
- Add recent work entries for 2025-12-05:
- Cloudflare TURN/STUN WebRTC integration
- Public pages (About Us, How It Works)
- Cloudflare Turnstile CAPTCHA
- Contact form and 404 page
- Responsive design improvements
- Add backend endpoint to fetch ICE server credentials from Cloudflare
- Implement dynamic ICE server configuration in frontend
- Add fallback to public STUN servers when Cloudflare unavailable
- Create comprehensive test suite for WebRTC API endpoint
- Update environment configuration with Cloudflare TURN credentials
Backend changes:
- New route: GET /api/webrtc/ice-servers (authenticated)
- Fetches temporary credentials from Cloudflare API with 24h TTL
- Returns formatted ICE servers for RTCPeerConnection
- Graceful fallback to Google STUN servers on errors
Frontend changes:
- Remove hardcoded ICE servers from useWebRTC hook
- Fetch ICE servers dynamically from backend on mount
- Store servers in ref for peer connection initialization
- Add webrtcAPI service for backend communication
Tests:
- 9 comprehensive tests covering all scenarios
- 100% coverage for webrtc.js route
- Tests authentication, success, and all fallback scenarios
Navigation:
- Reduce logo and text size on mobile (w-6 h-6 -> w-8 h-8 on sm+)
- Reduce spacing between nav items (space-x-2 -> space-x-4 on sm+)
- Hide 'Dashboard' text on mobile, show icon only
- Adjust padding and text sizes for all nav buttons
- Add whitespace-nowrap to 'Get Started' button
Hero section:
- Responsive heading sizes (text-3xl -> text-6xl)
- Responsive paragraph sizes (text-base -> text-2xl)
- Responsive padding (py-12 -> py-20)
- Responsive button sizes (px-6/py-3 -> px-8/py-4)
- Add flex-wrap to stats section to prevent overflow on small screens
- Make profile header flex-col on mobile, flex-row on larger screens
- Add flex-shrink-0 to icons to prevent them from shrinking
- Reduce padding on mobile (p-4) and increase on larger screens
- Add min-w-0 to prevent text overflow issues
- Desktop: username and avatar link to /u/{username}
- Mobile: username section in dropdown links to public profile
- Adds hover effect to indicate clickability
- Create HowItWorksPage component with markdown rendering
- Add how-it-works.md with Lorem Ipsum placeholder content
- Add /how-it-works route in App.jsx
- Add How It Works link to homepage footer (Product section)
- Create AboutUsPage component with markdown rendering
- Add react-markdown library for content rendering
- Create public/content directory for editable markdown files
- Add about-us.md with Lorem Ipsum placeholder content
- Create public/images/about directory for page images
- Add /about-us route in App.jsx
- Add About Us link to homepage footer
- Support for external links (open in new tab) and internal links
- Responsive image rendering with rounded corners and shadow
- Add Turnstile widget rendering in RegisterPage on step 2
- Implement programmatic widget initialization with callbacks
- Add token validation before form submission
- Update AuthContext and API service to pass turnstileToken
- Add backend verification via Cloudflare API in register controller
- Include client IP in verification request
- Add validation rule for turnstileToken
- Reset widget on registration error
- Add Turnstile script to frontend/index.html
- Implement programmatic widget rendering in ContactPage
- Add backend verification via Cloudflare API
- Include client IP in verification request
- Update CSP headers to allow Cloudflare resources
- Add environment variable configuration for site and secret keys
- Pass VITE_TURNSTILE_SITE_KEY to frontend container
- Add validation and error handling for CAPTCHA tokens
Changes for logged-in users:
- Top navigation: Show "Dashboard" and "Logout" buttons instead of "Sign In" and "Get Started"
- Hero CTA: Change main button to "Go to Dashboard" instead of "Start Collaborating"
- Hide bottom CTA section (registration prompt) for authenticated users
- Footer Account section: Show "Dashboard" and "Logout" instead of "Sign In" and "Register"
Other improvements:
- Removed "Explore Events" button from hero section
- Cleaned up footer: removed empty placeholder links (Features, How It Works, About, Privacy, Terms)
- Added "Support" section in footer with "Contact Us" link to /contact
- Simplified footer to 3 columns: Product (Events), Support (Contact Us), Account (dynamic based on auth)
Database changes:
- Added ContactMessage model to Prisma schema
- Fields: userId, username, firstName, lastName, email, subject, message, status, ipAddress
- Status enum: new, read, resolved
- Relation to User model
Backend changes:
- Added POST /api/public/contact endpoint for form submissions
- Works for both authenticated and non-authenticated users
- Validation for email, subject (3-255 chars), message (10-5000 chars)
- Activity logging for submissions
- Added admin endpoints:
- GET /api/admin/contact-messages - list with filtering by status
- GET /api/admin/contact-messages/:id - view single message (auto-marks as read)
- PATCH /api/admin/contact-messages/:id/status - update status
- DELETE /api/admin/contact-messages/:id - delete message
Frontend changes:
- Created ContactPage at /contact route
- For non-logged-in users: firstName, lastName, email, subject, message fields
- For logged-in users: auto-fills username, shows only email, subject, message
- Character counter for message (max 5000)
- Success screen with auto-redirect to homepage
- Created ContactMessagesPage at /admin/contact-messages
- Two-column layout: message list + detail view
- Filter by status (all, new, read, resolved)
- View message details with sender info and IP address
- Update status and delete messages
- Added admin dropdown menu to Navbar
- Desktop: dropdown with Activity Logs and Contact Messages
- Mobile: expandable submenu
- Click outside to close on desktop
- ChevronDown icon rotates when open
Note: CAPTCHA integration planned for future enhancement
Backend changes:
- Removed authentication requirement from GET /api/users/:username endpoint
- Removed authentication requirement from GET /api/users/:username/ratings endpoint
- These are public profile endpoints and should be accessible to all users
Frontend changes:
- PublicProfilePage now shows NotFoundPage component when user doesn't exist
- Unified 404 behavior: both invalid URLs and non-existent users show the same 404 page
- NotFoundPage "Requested URL" box now only shows in dev mode (import.meta.env.DEV)
- Removed unused AlertCircle icon import from PublicProfilePage
Backend Changes:
- Added public API endpoint /api/public/log-404 (no auth required)
- Created backend/src/routes/public.js for public endpoints
- Added ACTIONS.SYSTEM_404 and CATEGORIES.system to activity log service
- Registered public routes in app.js
Frontend Changes:
- Created NotFoundPage.jsx with standalone layout (no auth required)
- Added publicAPI.log404() to log 404 access attempts
- Logs both authenticated and anonymous users
- Changed profile route from /@:username to /u/:username
- Made profile route public (removed ProtectedRoute wrapper)
- Updated all profile links from /@${username} to /u/${username} in:
- ChatMessage.jsx
- DashboardMatchCard.jsx
- MatchRequestCards.jsx
- MatchCard.jsx
- UserListItem.jsx
- MatchChatPage.jsx
- PublicProfilePage.jsx
Fixes:
- React Router doesn't support @ in path segments
- 404 page now accessible to non-authenticated users without redirect
- Profile route no longer catches all unmatched routes
- Moved EventDetailsPage from /events/:slug/details to /admin/events/:slug/details
- Added admin authentication check with redirect to login/home
- Updated all navigation links across the app:
- EventsPage: "View details (admin)" button
- EventChatPage: "View QR Code (admin)" link
- EventCard: handleViewDetails navigation
- Fixed relative imports after moving to admin folder
This page contains admin-only features (QR codes, participants list,
matching config, scheduler config, matching runs) and should only be
accessible to administrators.
- Fixed issue where empty string success filter was interpreted as false
- Backend was filtering for only failed logs when success='' was sent
- Added check to skip sending success parameter when empty string
- Activity logs page now shows all logs when filters are set to 'All'
Bug: When user selected 'All' for success filter, frontend sent success='',
backend parsed this as success=false, showing only failed logs (usually none).
- Added app.set('trust proxy', 1) to allow Express to read proxy headers
- Enables proper client IP detection behind nginx reverse proxy
- Added /api/debug/ip endpoint for IP forwarding verification
Without trust proxy, Express ignores X-Forwarded-For and X-Real-IP headers,
causing all requests to appear from nginx container IP (172.x.x.x).
This fix ensures:
- Activity logs record correct client IPs
- Rate limiting works per actual client IP
- Security features function properly
- Changed regex /\. to /\.(git|svn|htaccess|htpasswd|env) to allow .vite directory
- Removed node_modules from nginx blocked paths for Vite dependency serving
- Set VITE_ALLOWED_HOSTS=all in development mode for Docker networking
Fixes issue where nginx was blocking Vite's pre-bundled dependencies in
/node_modules/.vite/deps/, causing 404 errors for React and other imports.
Document three recent chat improvements:
1. Real-time Active Users Fix (dd31761)
- Fixed bug where users list didn't update when new users joined
- Rewrote getAllDisplayUsers() to prioritize Socket.IO data
2. Message Length Limits (4a91a10)
- Added 2000 character limit with visual counter
- Backend + frontend validation
3. Spam Protection System (ace3311)
- Rate limiting: 10 messages per minute
- Duplicate detection within 60 second window
- Profanity filter with Polish + English words
Updated:
- README.md: Added chat features to Events & Chat section
- SESSION_CONTEXT.md: New "Chat Enhancements" section
- COMPLETED.md: Comprehensive entry with problem/solution/impact
- Last updated dates: 2025-12-03
Add comprehensive message validation with three protection mechanisms:
1. Rate Limiting: 10 messages per minute per user
2. Duplicate Detection: Prevents sending identical messages within 1 minute
3. Profanity Filter: Blocks inappropriate language (English + Polish)
Implementation:
- New messageValidation.js middleware with validateMessage() function
- Integrated into both event chat and match chat handlers
- Uses bad-words library (v2.0.0 for CommonJS compatibility)
- In-memory tracking with automatic cleanup every 5 minutes
- User-friendly error messages for each validation type
Technical details:
- Rate limit: 10 msg/min sliding window
- Duplicate check: Last 5 messages within 60s window
- Profanity: bad-words + 11 Polish words
- Memory management: Periodic cleanup of expired data
Added message length validation to prevent spam and improve UX with
character counter feedback.
Backend:
- Added MESSAGE_MAX_LENGTH constant (2000 characters)
- Validation in send_event_message handler:
- Check if content is string
- Check if not empty after trim
- Check if not exceeding max length
- Validation in send_match_message handler (same checks)
- Returns error message if validation fails
Frontend:
- Added MESSAGE_MAX_LENGTH constant (2000 characters)
- ChatInput component enhancements:
- maxLength attribute on input (hard limit)
- Character counter shows when >80% of limit
- Counter turns red when at limit
- Submit button disabled when at limit
- Counter format: "X/2000"
UX:
- User sees counter at 1600+ characters (80% of limit)
- Hard limit prevents typing beyond 2000 chars
- Clear visual feedback (red text) when at limit
- Consistent validation on both event and match chats
Security:
- Prevents spam with extremely long messages
- Protects against potential DoS via message size
- Database already uses TEXT type (supports limit)
Fixed issue where active users list in event chat did not update in
real-time when new users joined. Users had to refresh the page to see
newly joined participants.
Root Cause:
- getAllDisplayUsers() used checkedInUsers (loaded once from API) as
base list, with activeUsers (Socket.IO real-time) only for isOnline flag
- When new user joined chat, they appeared in activeUsers but not in
checkedInUsers, so they were not displayed
Solution:
- Rewrote getAllDisplayUsers() to prioritize activeUsers (real-time data)
- Merges activeUsers (online) with checkedInUsers (offline checked-in users)
- Uses Socket.IO data as source of truth for online users
- Enriches with database data when available (firstName, lastName, etc)
- Sorts online users first, offline second
Changes:
- EventChatPage.jsx: Rewrote getAllDisplayUsers() to merge activeUsers
with checkedInUsers, prioritizing real-time Socket.IO data
- useEventChat.js: Added debug logging for active_users events
- socket/index.js: Added debug logging for active_users emissions
Testing:
- User A in chat sees User B appear immediately when B joins
- No page refresh required
- Online/offline status updates in real-time
Updated all major documentation files to reflect the completed Activity
Log System (Phase 3.5) implementation.
Changes:
- README.md: Added Admin & Monitoring section, updated database schema
to 12 tables, added Activity Log System to completed phases, updated
project structure to show admin pages and services
- SESSION_CONTEXT.md: Updated recent work, added activity log system to
core features, updated database schema, added comprehensive Phase 3.5
overview with all implementation details
- COMPLETED.md: Added full Activity Log System entry with all 8 phases,
implementation details, git commits, and access information
Updated dates to 2025-12-02.
Complete implementation of admin activity logs dashboard with real-time
streaming capabilities. Admin users can now monitor all system activity
through a comprehensive web interface.
Features:
- Stats dashboard with 4 key metrics (total logs, unique users, failures, 24h activity)
- Category breakdown visualization with color-coded badges
- Advanced filtering (date range, category, action type, username, success/failure)
- Paginated log table (50 entries per page) with sort by timestamp
- Real-time streaming toggle using Socket.IO
- Color-coded action badges (blue=auth, green=event, purple=match, red=admin, yellow=chat)
- Admin-only access with automatic redirect for non-admin users
- Responsive design for mobile and desktop
Frontend Changes:
- Created ActivityLogsPage.jsx (600+ lines) with complete UI implementation
- Added 3 admin API methods to api.js (getActivityLogs, getActivityLogActions, getActivityLogStats)
- Added /admin/activity-logs route to App.jsx
- Added admin navigation link to Navbar (desktop & mobile) with Shield icon
- Only visible to users with isAdmin flag
Implementation Details:
- Uses getSocket() from socket service for real-time updates
- Joins 'admin_activity_logs' Socket.IO room on streaming enable
- Receives 'activity_log_entry' events and prepends to table (first page only)
- Comprehensive error handling and loading states
- Empty states for no data
- Clean disconnect handling when streaming disabled
Testing:
- Build successful (no errors)
- Ready for manual testing and verification
Phase 8 (Testing) remains for manual verification of all features.
Added 3 admin-only endpoints for activity log management:
1. GET /api/admin/activity-logs
- Query logs with comprehensive filtering
- Supports: date range, action, category, username, userId, success
- Pagination with limit/offset
- Logs ADMIN_VIEW_LOGS action
- Returns: logs array, total count, pagination info
2. GET /api/admin/activity-logs/actions
- Get list of unique action types
- Returns: action names and categories
- Useful for filter dropdowns in UI
3. GET /api/admin/activity-logs/stats
- Dashboard statistics
- Returns: total logs, unique users, failed actions,
success rate, logs by category, recent activity (24h)
All endpoints protected with authenticate + requireAdmin middleware.
Database changes:
- Add ActivityLog model for comprehensive activity tracking
- Track all user actions (auth, events, matches, admin)
- Denormalized username for query performance
- JSON metadata for flexibility
- Multiple indexes for common filter patterns
- Add isAdmin flag to User model for admin access control
- Add activityLogs relation to User
Schema pushed to database with prisma db push
Created initial admin user: spotlight@radziel.com
This is Phase 1 of Activity Log System implementation.
Next phases: backend service, middleware, API endpoints, frontend UI.
- Move email templates to separate files in src/emails/templates/
- Create new email service architecture (service.js, index.js)
- Add recording suggestions email template for matching notifications
- Integrate email notifications with matching system (sends when suggestions created)
- Update controllers (auth.js, user.js) to use new email module
- Update tests to use new email module path
- Remove deprecated src/utils/email.js
Features:
- Template-based email system for easy editing
- Automatic email notifications when recording assignments are made
- Clean separation between template logic and sending logic
- Graceful error handling for AWS SES failures
S15.1-15.2: Rate Limiting & Spam Protection
- Add max 20 pending outgoing match requests limit
- Implement rate limiter: 10 match requests per minute per user
- Return 429 status with clear error messages
S16.1: Socket Notifications for New Suggestions
- Emit 'recording_suggestions_created' event when matching creates suggestions
- Notify only assigned recorders (not NOT_FOUND status)
- Group suggestions by recorder for efficiency
- Include event details and suggestion count
Implementation:
- backend/src/routes/matches.js: Rate limiter + pending limit check
- backend/src/services/matching.js: Socket notifications in saveMatchingResults
- backend/src/__tests__/spam-protection-notifications.test.js: 8 test cases
Test coverage:
- TC1-TC3: Max pending requests (spam protection)
- TC4-TC5: Rate limiting (10/min)
- TC6-TC8: Socket notifications for new suggestions
- Update README.md with current test statistics (342/342 tests passing)
- Add detailed breakdown of all matching/ratings test suites
- Create comprehensive TESTING_MATCHING_RATINGS.md guide covering all 45 tests
- Document matching runs audit, incremental matching, and scheduler features
- Add code coverage highlights and test scenarios
- Updated event_message test to expect message.user.username
- Matches current socket.io implementation which nests user data
- All 342 tests now passing (100%)
Backend:
- Extend GET /api/matches to include RecordingSuggestions alongside Match objects
- Add 'type' field: 'manual' for user-created matches, 'auto' for algorithm suggestions
- Fetch suggestions where user is dancer (to be recorded) or recorder (recording others)
- Transform suggestions to match format with partner info
- Support status filtering for both types
Frontend:
- Display 'Auto' (purple) or 'Manual' (gray) badge on match cards
- For pending auto suggestions: show 'Go to Records' button instead of Accept/Reject
- For accepted auto suggestions without slug: show 'Chat not available yet'
- Only allow Accept/Reject actions on manual match requests