Commit Graph

112 Commits

Author SHA1 Message Date
Radosław Gierwiało
e2b10387c2 feat(beta): add beta testing features and privacy policy page
Implemented comprehensive beta testing system with tier badges and
reorganized environment configuration for better maintainability.

Beta Testing Features:
- Beta banner component with dismissible state (localStorage)
- Auto-assign SUPPORTER tier to new registrations (env controlled)
- TierBadge component with SUPPORTER/COMFORT tier display
- Badge shown in Navbar, ProfilePage, and PublicProfilePage
- Environment variables: VITE_BETA_MODE, BETA_AUTO_SUPPORTER

Environment Configuration Reorganization:
- Moved .env files from root to frontend/ and backend/ directories
- Created .env.{development,production}{,.example} structure
- Updated docker-compose.yml to use env_file for frontend
- All env vars properly namespaced and documented

Privacy Policy Implementation:
- New /privacy route with dedicated PrivacyPage component
- Comprehensive GDPR/RODO compliant privacy policy (privacy.html)
- Updated CookieConsent banner to link to /privacy
- Added Privacy Policy links to all footers (HomePage, PublicFooter)
- Removed privacy section from About Us page

HTML Content System:
- Replaced react-markdown dependency with simple HTML loader
- New HtmlContentPage component for rendering .html files
- Converted about-us.md and how-it-works.md to .html format
- Inline CSS support for full styling control
- Easier content editing without React knowledge

Backend Changes:
- Registration auto-assigns SUPPORTER tier when BETA_AUTO_SUPPORTER=true
- Added accountTier to auth middleware and user routes
- Updated public profile endpoint to include accountTier

Files:
- Added: frontend/.env.{development,production}{,.example}
- Added: backend/.env variables for BETA_AUTO_SUPPORTER
- Added: components/BetaBanner.jsx, TierBadge.jsx, HtmlContentPage.jsx
- Added: pages/PrivacyPage.jsx
- Added: public/content/{about-us,how-it-works,privacy}.html
- Modified: docker-compose.yml (env_file configuration)
- Modified: App.jsx (privacy route, beta banner)
- Modified: auth.js (auto SUPPORTER tier logic)
2025-12-06 11:50:28 +01:00
Radosław Gierwiało
a786b1d92d feat(analytics): integrate Google Analytics 4 with GDPR compliance
Prepared the application for Google Analytics 4 tracking with full
GDPR/RODO compliance. GA only loads after user explicitly accepts cookies.

Features:
- Automatic page view tracking on route changes
- Custom event tracking for key user actions
- Privacy-first: GA loads only after cookie consent
- Easy configuration via environment variable
- Comprehensive tracking utilities for common events

Implementation:
- Created analytics.js with GA initialization and event tracking functions
- Created usePageTracking hook for automatic page view tracking
- Integrated GA into App.jsx with AnalyticsWrapper component
- Updated CookieConsent to initialize GA after user consent
- Added VITE_GA_MEASUREMENT_ID to .env.example

Custom events tracked:
- login, sign_up (authentication)
- match_request, match_accepted (matching)
- webrtc_connection, file_transfer (WebRTC)
- event_join, recording_suggestion (events/recording)
- search (search functionality)

Setup:
1. Add VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX to .env
2. Restart frontend container
3. GA will auto-load after user accepts cookies

Documentation:
- Created comprehensive setup guide in docs/GOOGLE_ANALYTICS_SETUP.md
- Includes troubleshooting, debugging tips, and usage examples
2025-12-05 22:28:00 +01:00
Radosław Gierwiało
3523172ecb feat(compliance): add GDPR/RODO compliant cookie consent banner
Implemented cookie consent banner to comply with EU regulations (GDPR/RODO).
The banner appears on first visit and stores user preference in localStorage.

Features:
- Non-intrusive bottom banner with clear messaging
- Accept/Decline options for user choice
- Link to privacy policy in About Us page
- Responsive design for mobile and desktop
- Auto-dismisses after consent with 1s delay on first show
- High z-index to stay above all content

Also added comprehensive Privacy & Cookies section to About Us page
explaining:
- What cookies we use (essential, analytics, preferences)
- How we handle user data
- GDPR/RODO compliance statements
- Contact information for privacy questions

Changes:
- Created CookieConsent component with modern UI
- Integrated banner into App.jsx
- Updated about-us.md with privacy policy section
2025-12-05 22:22:23 +01:00
Radosław Gierwiało
2cab8c3eba feat(participants): add profile links to usernames in Participants tab
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
2025-12-05 22:18:33 +01:00
Radosław Gierwiało
229aafc8e9 refactor(recordings): remove manual matching trigger from Recording tab
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
2025-12-05 22:16:49 +01:00
Radosław Gierwiało
76be8a4419 refactor(frontend): replace confirm() with modern confirmation modals
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.
2025-12-05 22:14:09 +01:00
Radosław Gierwiało
bb8a876ab0 refactor(frontend): replace alert() with modern toast notifications
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.
2025-12-05 22:09:37 +01:00
Radosław Gierwiało
3ae9fd149b feat(frontend): add unified header and footer to public pages
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
2025-12-05 21:59:56 +01:00
Radosław Gierwiało
a92d7469e4 feat(webrtc): integrate Cloudflare TURN/STUN servers
- 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
2025-12-05 21:23:50 +01:00
Radosław Gierwiało
e1138c789e fix(homepage): improve responsive layout for mobile devices
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)
2025-12-05 18:42:52 +01:00
Radosław Gierwiało
00825d56b6 fix(profile): improve responsive layout for public profile
- 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
2025-12-05 18:40:13 +01:00
Radosław Gierwiało
fb4e0bea99 refactor(profile): remove Matches count from public profile stats
- Remove Matches stat display from public profile
- Keep only Rating and Reviews stats visible
- Remove unused Users icon import
2025-12-05 18:38:14 +01:00
Radosław Gierwiało
d4dd7bac30 feat(navbar): make username clickable link to public profile
- Desktop: username and avatar link to /u/{username}
- Mobile: username section in dropdown links to public profile
- Adds hover effect to indicate clickability
2025-12-05 18:36:51 +01:00
Radosław Gierwiało
1f763d4558 refactor(footer): remove Events link from Product section 2025-12-05 18:34:51 +01:00
Radosław Gierwiało
c4372930dd feat(content): add How It Works page with markdown support
- 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)
2025-12-05 18:33:25 +01:00
Radosław Gierwiało
2c5689ac8e feat(content): add About Us page with markdown content support
- 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
2025-12-05 18:30:44 +01:00
Radosław Gierwiało
d8085f828f feat(security): add Cloudflare Turnstile CAPTCHA to registration form
- 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
2025-12-05 18:20:26 +01:00
Radosław Gierwiało
f3b8156557 feat(security): implement Cloudflare Turnstile CAPTCHA on contact form
- 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
2025-12-05 18:08:05 +01:00
Radosław Gierwiało
25042d0fec feat(home): adapt homepage for authenticated users
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)
2025-12-05 17:21:55 +01:00
Radosław Gierwiało
34f18b3b50 feat(contact): add contact form with admin panel and navbar dropdown
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
2025-12-05 17:15:25 +01:00
Radosław Gierwiało
f90945aa47 fix(profiles): make public profiles accessible without authentication and unify 404 behavior
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
2025-12-05 16:54:38 +01:00
Radosław Gierwiało
948c694ed6 feat(system): implement 404 page with activity logging and change profile route format
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
2025-12-03 20:27:51 +01:00
Radosław Gierwiało
eb5aacd797 refactor(admin): move event details page to admin section
- 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.
2025-12-03 19:52:00 +01:00
Radosław Gierwiało
179aaa8f16 fix(admin): activity logs empty success filter showing no results
- 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).
2025-12-03 19:39:16 +01:00
Radosław Gierwiało
4a91a10aff feat(chat): add 2000 character limit for messages
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)
2025-12-02 23:46:54 +01:00
Radosław Gierwiało
dd3176196e fix(chat): real-time active users list updates
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
2025-12-02 23:38:46 +01:00
Radosław Gierwiało
1051cc6754 feat(admin): implement activity logs frontend page (Phase 6-7)
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.
2025-12-02 23:17:19 +01:00
Radosław Gierwiało
0466ef9e5c security(nginx): block access to sensitive files and directories
Added comprehensive security rules to all nginx configurations:

Dev proxy (nginx/conf.d/default.conf):
- Block dotfiles and .git directory
- Block .env* files and config files
- Block node_modules, docker configs, package files
- Block backup files (.bak, .swp, ~)

Prod proxy (nginx/conf.d.prod/default.conf):
- All dev security rules
- Added security headers (X-Frame-Options, CSP, etc.)
- Strict CSP with frame-ancestors 'none'

Frontend container (frontend/nginx.conf):
- Block dotfiles and sensitive config files
- Block node_modules and build configs
- Added security headers

All blocked paths return 404 and disable logging to avoid log spam.

Blocks: .env*, .git, node_modules, docker*, package*.json,
prisma, backup*, *.bak, *.swp, tsconfig.json, and more.
2025-12-02 19:55:08 +01:00
Radosław Gierwiało
f45cadae7d feat(matches): show both manual match requests and auto recording suggestions
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
2025-11-30 15:30:49 +01:00
Radosław Gierwiało
d8799d03af feat(dashboard): add Recording Assignments section
- Extend dashboard API to include recordingSuggestions for each event
- Add toBeRecorded and toRecord arrays with heat and user details
- Export RecordingSummaryCard component
- Add Recording Assignments section to DashboardPage
- Filter and display events with recording suggestions
- Show up to 2 suggestions per event with View Details link
2025-11-30 15:14:06 +01:00
Radosław Gierwiało
6ce3111cdd feat(dashboard): improve RecordingSummaryCard styling and fix tab navigation
- Increase font size from xs to sm for better readability
- Reduce avatar size from xs to 24px for better proportions
- Add proper layout with heat names in separate line
- Add truncate for long usernames to prevent overflow
- Style status badges with colored backgrounds and icons (pending/accepted)
- Fix EventChatPage to read and handle ?tab=records URL parameter
- Map 'records' query param to 'recording' tab for proper navigation
2025-11-30 15:13:50 +01:00
Radosław Gierwiało
2e49fa5c62 feat(recordings): only recorder can accept/reject suggestions in MVP
Backend changes:
- Restrict suggestion status updates to recorder only
- Dancers can now only view who is assigned to record them
- Return 403 error if non-recorder tries to update status

Frontend changes:
- Remove Accept/Reject buttons from dancer view (TO_BE_RECORDED)
- Add "Pending" status badge with clock icon for pending suggestions
- Keep Accept/Reject buttons for recorder view (TO_RECORD)
- Dancers see only status badge and optional chat button

UX flow:
- Dancer sees: "Recording you: @username [Pending]"
- Recorder sees: "You record: @username [Accept] [Reject]"
- Only recorder's action creates the Match
2025-11-30 14:54:09 +01:00
Radosław Gierwiało
560ff1edc1 fix(scheduler): implement deadline-based matching with 5-run limit and fix security issues
Security fixes:
- Replace $queryRawUnsafe with parameterized $queryRaw in admin.js to prevent SQL injection
- Use PostgreSQL ANY() operator for safe array parameter handling

Scheduler improvements:
- Add registrationDeadline support - scheduler now waits until deadline before running
- Implement 5-run limit after deadline (runs exactly 5 times with 5-minute intervals)
- Add countScheduledRunsAfterDeadline() to track post-deadline runs
- Add environment variable validation with sensible min/max ranges
- Fix Prisma query syntax (remove invalid endDate null check for non-nullable field)

UI improvements:
- Fix colspan mismatch in MatchingRunsSection (6 → 8 columns)
- Remove duplicate "Uruchom Matching" button, keep only "Run now" with audit tracking
- Simplify MatchingConfigSection to focus on deadline configuration

Logging enhancements:
- Add detailed scheduler logs showing run progress (e.g., "Running post-deadline matching (3/5)")
- Log wait times before deadline and between runs
- Show completion status after 5 runs
2025-11-30 14:42:08 +01:00
Radosław Gierwiało
752d65035a fix(scheduler): use equals: null filter for endDate to satisfy Prisma where syntax and stop log spam 2025-11-30 13:50:48 +01:00
Radosław Gierwiało
621511fccf feat(matching-runs): add per-run aggregate stats and UI display
- Admin list endpoint returns totalSuggestions, assignedCount, aggregatedNotFoundCount per run
- UI: show Total/Matched/Not found columns using fresh aggregates
- Add anchor link Run #ID and wording 'Pairs created in this run'
2025-11-30 13:43:05 +01:00
Radosław Gierwiało
a9ad25eb38 feat(matching-runs): attach origin_run_id to new suggestions and expose pairs-per-run API
- Extend saveMatchingResults(eventId, suggestions, runId) and set originRunId
- Scheduler/Admin run-now: always pass runId
- Admin API: GET /api/admin/events/:slug/matching-runs/:runId/suggestions
- Prisma: add compound index on (origin_run_id, status)
- Frontend: add getRunSuggestions, expand row in MatchingRunsSection with 'Pairs created in this run' wording
2025-11-30 13:37:32 +01:00
Radosław Gierwiało
7e2a196f99 feat(frontend): add Run now button and matching runs list on event details page
- New adminAPI for run-now and runs listing
- MatchingRunsSection with refresh and run controls
- Integrate into EventDetailsPage under matching configuration
2025-11-30 13:20:33 +01:00
Radosław Gierwiało
8c753a7148 feat: add match data to suggestions and chat link for accepted recordings
Backend changes:
- Modified getUserSuggestions to include match data (id, slug, status)
- Returns match info for both toBeRecorded and toRecord suggestions

Frontend changes:
- Added useNavigate hook to RecordingTab
- Capture match data from updateSuggestionStatus response
- Added MessageCircle icon and chat button to SuggestionCard
- Show "Open Chat" button for accepted suggestions with active matches
- Navigate to /matches/{matchSlug}/chat when clicked

This completes the recording stats flow by allowing users to easily
access the match chat after accepting a recording suggestion.
2025-11-30 11:03:29 +01:00
Radosław Gierwiało
a9c46f552f feat: add @ prefix to profile URLs and make usernames clickable
- Updated all profile links to use /@username format
- Made usernames clickable in chat messages
- Added URL parameter sanitization to strip @ when fetching user data
- Ensures consistent profile URL format across the application
2025-11-29 20:57:17 +01:00
Radosław Gierwiało
4e9557bd29 feat(chat): add country flags and competitor numbers with normalized data architecture
Implemented display of country flags and competitor numbers in event chat messages:
- Country flags displayed as emoji (🇸🇪, 🇵🇱, etc.) with proper emoji font support
- Competitor numbers shown in #123 format next to usernames
- Normalized data architecture with user and participant caches on frontend
- User data (username, avatar, country) and participant data (competitorNumber) cached separately
- Messages store only core data (id, content, userId, createdAt)
- Prevents data inconsistency when users update profile information
- Fixed duplicate message keys React warning with deduplication logic
- Backend sends nested user/participant objects for cache population
- Auto-updates across all messages when user changes avatar or country

Backend changes:
- Socket.IO event_message and message_history include nested user/participant data
- API /events/:slug/messages endpoint restructured with same nested format
- Batch lookup of competitor numbers for efficiency

Frontend changes:
- useEventChat hook maintains userCache and participantCache
- ChatMessage component accepts separate user/participant props
- ChatMessageList performs cache lookups during render
- Emoji font family support for cross-platform flag rendering
2025-11-29 19:49:06 +01:00
Radosław Gierwiało
c575ef6dc1 feat(frontend): add page titles to navbar on mobile
- Save vertical space on mobile by showing page title in navbar
- Mobile: "spotlight.cam - {PageTitle}" instead of separate h1
- Desktop: unchanged - page titles remain as separate headings
- Saves ~60-80px vertical space on mobile devices

Pages updated:
- Dashboard, Events, Matches, History, Profile
2025-11-29 16:12:47 +01:00
Radosław Gierwiało
58044e1d02 fix(frontend): ensure consistent width across all matches tabs
- Add w-full to MatchCard for consistent card width
- Use Layout noPadding to avoid padding conflicts
- Add explicit padding and width control to main container
- Ensure All, Pending, and Active tabs have identical width
2025-11-29 15:56:12 +01:00
Radosław Gierwiało
420209c037 fix(frontend): add error message display on login page
- Show error alert when login fails instead of console-only logging
- Display user-friendly error message: "Invalid email or password"
- Clear error state before new login attempt
- Use existing Alert component for consistency with RegisterPage
2025-11-29 15:26:40 +01:00
Radosław Gierwiało
634cd97032 refactor(frontend): simplify event chat UI
- Replace "Edit heats" button with icon-only version
- Remove connection status indicator (show "Disconnected" warning only when offline)
- Remove event location from header
- Hide Participants tab on desktop (sidebar already visible)
- Remove "Participants" header from sidebar on desktop
2025-11-29 15:18:22 +01:00
Radosław Gierwiało
dfb5313f9a refactor(frontend): implement mobile-first layout for chat pages
- Add fullWidth prop to Layout component for chat pages
- Redesign EventChatPage and MatchChatPage with fixed layout:
  - Navbar sticky to top (no gaps)
  - Event/partner header directly below navbar
  - Chat content fills available space (flex-1)
  - Input area fixed to bottom
  - Full screen width on mobile (no margins)
- Translate RecordingTab UI strings to English
- Move Leave Event button to header
- Remove unnecessary margins and max-width constraints

This provides a better mobile experience with full-screen chat
interface similar to native messaging apps.
2025-11-29 15:04:41 +01:00
Radosław Gierwiało
408317b974 refactor(frontend): add CONNECTION_STATE and SUGGESTION_TYPE constants
- Add CONNECTION_STATE (disconnected, connecting, connected, failed)
- Add SUGGESTION_TYPE (toBeRecorded, toRecord)
- Update useWebRTC.js to use CONNECTION_STATE
- Update MatchChatPage.jsx to use CONNECTION_STATE
- Update RecordingTab.jsx to use SUGGESTION_TYPE
2025-11-23 22:28:54 +01:00
Radosław Gierwiało
b3a6d39d7a refactor(frontend): replace status string literals with constants
- Create constants/statuses.js with MATCH_STATUS, SUGGESTION_STATUS, MATCH_FILTER
- Update MatchCard, MatchesPage, HistoryPage, RatePartnerPage to use MATCH_STATUS
- Update RecordingTab to use SUGGESTION_STATUS
- Update Navbar to use MATCH_STATUS for API calls
2025-11-23 22:21:12 +01:00
Radosław Gierwiało
93ff331bfb refactor(frontend): extract ProfileForm and PasswordChangeForm from ProfilePage
- Create components/profile/ProfileForm.jsx (192 lines)
- Create components/profile/PasswordChangeForm.jsx (99 lines)
- Create components/profile/index.js barrel export
- Reduce ProfilePage.jsx from 394 → 84 lines (-79%)
2025-11-23 22:13:56 +01:00
Radosław Gierwiało
185c485ec7 refactor(frontend): extract MatchCard component from MatchesPage
- Create components/matches/MatchCard.jsx (119 lines)
- Create components/matches/index.js barrel export
- Reduce MatchesPage.jsx from 349 → 240 lines (-31%)
2025-11-23 22:11:43 +01:00
Radosław Gierwiało
8e17c10353 refactor(frontend): extract DashboardPage into components
Split DashboardPage (578 lines) into focused components:
- DashboardEventCard: event card with chat access
- DashboardMatchCard: match card with status indicators
- MatchRequestCards: incoming/outgoing request cards
- EmptyState: reusable empty state component (in common/)

DashboardPage now 295 lines (-49%)
2025-11-23 22:08:16 +01:00