The issue was that handleEditLesson was setting lessonForm.chapters to a direct
reference of lesson.chapters instead of creating a copy. This caused mutations
to the form to also modify the lesson objects in the lessons array state.
When editing lesson 3 with chapters, then switching to edit lesson 2 (which had
no chapters), the form would still show lesson 3's chapters because it was
referencing the same array object.
Fix: Use spread operator [...lesson.chapters] to create a shallow copy of the
chapters array, preventing shared references between form state and lessons state.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The root cause was that ProtectedRoute and Auth.tsx were checking user.user_metadata?.role,
but the admin role is stored in the user_roles table, not in user metadata.
Changes:
- ProtectedRoute: Use isAdmin flag from useAuth context instead of user.user_metadata?.role
- Auth.tsx: Use isAdmin flag for role-based redirect logic
- Remove redundant auth checks from individual admin/member pages (ProtectedRoute handles it)
- Add isAdmin to useEffect dependencies to ensure redirect happens after admin check completes
This fixes the issue where admins were being redirected to /dashboard instead of /admin
after login, because the role check was happening before the async admin role lookup completed.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Removed hasChecked ref logic that was causing rendering issues. The key insight is that the effect should run when user changes, but only redirect if the condition doesn't match.
## Changes:
- Remove hasCheckedRef complexity
- Simplify effect to only redirect when conditions don't match
- Split admin check into separate condition that only runs when user exists
- This prevents unnecessary redirects while allowing normal rendering
## Root Cause:
The hasChecked logic was preventing the component from rendering properly on navigation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed from useState to useRef for hasChecked flag to prevent loading state on route navigation. Each ProtectedRoute instance now properly renders immediately after auth check completes.
## Changes:
- Use useRef instead of useState for hasChecked flag
- Remove !hasChecked from loading condition
- This allows immediate rendering after first auth check
- Prevents blank pages when navigating between admin routes
## Technical Details:
- useState caused each new route to start with hasChecked=false
- useRef persists the check but each route instance is independent
- Combined with removing !hasChecked from loading check, pages render immediately
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed infinite redirect loop and navigation blocking in ProtectedRoute component. The issue was that the useEffect was running on every render when navigating between admin routes.
## Changes:
- Added hasChecked state to ensure effect only runs once per mount
- Prevents multiple redirects and navigation blocking
- Allows smooth navigation between admin and member pages after login
## Technical Details:
- Before: useEffect ran on every render with requireAdmin in dependencies
- After: useEffect runs once when auth loading completes
- This prevents React Router navigation conflicts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed timeline accordion behavior to keep all lesson chapters collapsed on page load. This prevents overwhelming users with too much content when viewing bootcamp curriculum previews.
## Before:
- All lesson timelines expanded by default
- 3 lessons × 8 chapters = 24+ items visible (~1500px+ scroll)
- Overwhelming visual clutter on product detail pages
## After:
- All timelines collapsed by default
- Shows "N timeline items" hint for each lesson
- Users can expand individual lessons they're interested in
- Cleaner, more professional appearance
- Better for conversion - product details remain prominent
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
CRITICAL SECURITY FIX: All admin and member routes now require authentication.
## Changes:
- Created ProtectedRoute component to enforce authentication
- Protected all member routes (/dashboard, /access, /orders, /profile)
- Protected all admin routes (/admin/*) with admin role check
- Added redirect-after-login functionality using sessionStorage
- Non-authenticated users accessing protected pages are redirected to /auth
- Non-admin users accessing admin pages are redirected to /dashboard
## Security Impact:
- Prevents unauthorized access to admin panel and member areas
- Users must login to access any protected functionality
- Admin routes additionally verify user role is 'admin'
- After login, users are redirected back to their intended page
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed bug where editing a lesson without chapters would show chapters from the previously edited lesson. The handleNewLesson function now explicitly initializes chapters to an empty array to prevent state from carrying over between lesson edits.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added state for expanded lesson chapters (expandedLessonChapters)
- Created toggleLessonChapters helper function
- Fixed formatChapterTime to support hours (1:11:11 instead of 111:11)
- Wrapped lesson timeline in Collapsible component with Clock icon
- Timeline header shows "N timeline items" count
- All timelines expanded by default on page load
- Users can collapse/expand to focus on one lesson at a time
- ChevronDown/ChevronRight icons indicate expanded/collapsed state
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created migration to enable public read access to bootcamp curriculum
- RLS policies on bootcamp_modules and bootcamp_lessons for public/authenticated roles
- Allows kurikulum card to display on product detail pages for unauthenticated users
- Users can now see curriculum preview before purchasing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Change jumpToTime to check video.seekable instead of adiloPlayer.isReady
- Wait for canplay event if video is not seekable yet
- This fixes issue where resume button started from 00:00 instead of saved position
- Added better console logging for debugging
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Pass playerRef, currentTime, accentColor, and setCurrentTime to VideoPlayer
- This fixes "Cannot read properties of undefined (reading 'current')" error
- Timeline chapter items can now successfully jump to specific timestamps
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Moved useMemo calls before early returns in VideoPlayer component
- This ensures hooks are always called in the same order on every render
- Fixed violation of Rules of Hooks that caused error in production
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Moved VideoPlayer component outside main Bootcamp component to prevent re-creation on every render
- Memoized video source object and URL values to ensure stability
- Removed duplicate Bootcamp component declaration that caused build failure
- Video player now persists across Bootcamp re-renders, fixing continuous reload from 00:00
- Timeline clicking now works correctly without triggering video reload
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Move VideoPlayer component outside Bootcamp component to prevent re-creation on every render
- This prevents useAdiloPlayer and all hooks from re-initializing unnecessarily
- Video now plays and jumps correctly without reloading
- Component structure now matches WebinarRecording page pattern
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Memoize video source object in Bootcamp to prevent unnecessary re-renders
- Reset resume prompt flag when videoId changes to allow prompt for new lessons
- Remove key prop from VideoPlayerWithChapters to prevent unmounting
- Video now plays correctly without reloading on every interaction
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove border-bottom from TimelineChapters component for better readability
- Revert collapsible timeline changes from bootcamp sidebar
- Fix video reloading issue by adding key prop to force remount on lesson change
- Prevent resume prompt from showing multiple times during video playback
- Resume prompt now only shows once when component mounts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add 'content' field to fetchProducts SELECT query
- Fixes Rich Text Editor showing empty when editing products
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Email System Fixes:
- Fix email sending after payment: handle-order-paid now calls send-notification
instead of send-email-v2 directly, properly processing template variables
- Fix order_created email timing: sent immediately after order creation,
before payment QR code generation
- Update email templates to use short order ID (8 chars) instead of full UUID
- Add working "Akses Sekarang" buttons to payment_success and access_granted emails
- Add platform_url column to platform_settings for email links
OTP Verification Flow:
- Create dedicated /confirm-otp page for users who close registration modal
- Add link in checkout modal and email to dedicated OTP page
- Update OTP email template with better copywriting and dedicated page link
- Fix send-auth-otp to fetch platform settings for dynamic brand_name and platform_url
- Auto-login users after OTP verification in checkout flow
Admin Features:
- Add delete user functionality with cascade deletion of all related data
- Update IntegrasiTab to read/write email settings from platform_settings only
- Add test email template for email configuration testing
Cleanup:
- Remove obsolete send-consultation-reminder and send-test-email functions
- Update send-email-v2 to read email config from platform_settings
- Remove footer links (Ubah Preferensi/Unsubscribe) from email templates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The early termination issue was caused by the page navigating away before
the send-notification function call could complete. Changed from fire-and-forget
to awaiting the email send result before redirecting.
Changes:
- Checkout.tsx: Changed send-notification call to use await instead of .then()/.catch()
- Now waits for email send to complete before navigating to order detail page
- Email failures are caught in try/catch but navigation still proceeds
Technical details:
- Browser was terminating pending requests when navigate() was called immediately
- Early termination: isolate warning indicated function was being killed mid-execution
- Awaiting the function call ensures it completes before page navigation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhanced email notification system to send order confirmation emails immediately after order creation with embedded QR code for payment.
Changes:
- Checkout.tsx: Added send-notification call after payment creation with comprehensive logging
- send-notification: Added QRCode library integration for generating base64 QR images for order_created emails
- NotifikasiTab.tsx: Added QR code section to default order_created template and updated shortcodes list
Technical details:
- QR code generated as base64 data URL for email client compatibility
- Fire-and-forget pattern ensures checkout flow isn't blocked
- Added detailed console logging for debugging email send issues
- New shortcodes: {qr_code_image}, {qr_expiry_time}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The auth functions were being called but not destructured from useAuth hook,
causing ReferenceError at runtime.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add clickable prop to TimelineChapters component for marketing vs purchased users
- Make timeline non-clickable in product preview pages with lock icons
- Keep timeline clickable in actual content pages (WebinarRecording, Bootcamp)
- Add inline auth modal in checkout with login/register tabs
- Replace "Login untuk Checkout" button with seamless auth flow
- Add form validation and error handling for auth forms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Create supabase/shared/email-template-renderer.ts for code reuse
- Update send-auth-otp to import from shared file (eliminates 260 lines of duplication)
- Add isResendOTP state to track existing user email confirmation
- Update login error handler to detect unconfirmed email
- Show helpful message when user tries to login with unconfirmed email
This addresses:
1. Code duplication between src/lib and edge functions
2. User experience for unconfirmed email login attempts
- Add early returns for better error handling flow
- Add console.log for SignUp result to debug user creation
- Ensure loading state is always reset properly
- Add explicit check for missing user data after signUp
Added a "Kembali ke Beranda" (Back to Home) button on the login/signup
page to allow users to navigate back to the home page without needing
to authenticate.
Changes:
- Imported Link and ArrowLeft icon from lucide-react
- Added button above the auth card that links to "/"
- Wrapped content in a space-y-4 container for proper spacing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit implements displaying lesson chapters/timeline as marketing content
on the Product Detail page for bootcamp products, helping potential buyers
understand the detailed breakdown of what they'll learn.
## Changes
### Product Detail Page (src/pages/ProductDetail.tsx)
- Updated Lesson interface to include optional chapters property
- Modified fetchCurriculum to fetch chapters along with lessons
- Enhanced renderCurriculumPreview to display chapters as nested content under lessons
- Chapters shown with timestamps and titles, clickable to navigate to bootcamp access page
- Visual hierarchy: Module → Lesson → Chapters with proper indentation and styling
### Review System Fixes
- Fixed review prompt re-appearing after submission (before admin approval)
- Added hasSubmittedReview check to prevent showing prompt when review exists
- Fixed edit review functionality to pre-populate form with existing data
- ReviewModal now handles both INSERT (new) and UPDATE (edit) operations
- Edit resets is_approved to false requiring re-approval
### Video Player Enhancements
- Implemented Adilo/Video.js integration for M3U8/HLS playback
- Added video progress tracking with refs pattern for reliability
- Implemented chapter navigation for both Adilo and YouTube players
- Added keyboard shortcuts (Space, Arrows, F, M, J, L)
- Resume prompt for returning users with saved progress
### Database Migrations
- Added Adilo video support fields (m3u8_url, mp4_url, video_host)
- Created video_progress table for tracking user watch progress
- Fixed consulting slots user_id foreign key
- Added chapters support to products and bootcamp_lessons tables
### Documentation
- Added Adilo implementation plan and quick reference docs
- Cleaned up transcript analysis files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added robust error handling for Plyr player instance:
- Check if player.on method exists before using events
- Fallback to polling time updates every 500ms if events unavailable
- Use setInterval to wait for player initialization
- Properly cleanup intervals on component unmount
- Prevents "L.on is not a function" error
This ensures the video player works even if Plyr's event system
has issues initializing, using a reliable polling fallback.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Rewrote VideoPlayerWithChapters component using plyr-react with best practices:
- Use Plyr React component wrapper for proper integration
- Aggressive YouTube UI hiding with correct parameters (controls=0, disablekb=1, fs=0)
- Block right-click context menu globally
- Block developer tools shortcuts (F12, Ctrl+Shift+I/J, Ctrl+U)
- CSS pointer-events manipulation to block YouTube iframe interactions
- Only Plyr controls remain interactive
- Accurate time tracking via Plyr's timeupdate event
- Chapter jump functionality via Plyr's currentTime API
- Custom accent color support for Plyr controls
- Hide YouTube's native play button with ::after overlay
This implementation provides:
✅ Working timeline with accurate duration tracking
✅ Chapter navigation that jumps to correct timestamps
✅ Maximum prevention of YouTube UI access
✅ Custom Plyr controls with your accent color
✅ Right-click and dev tools blocking
✅ No "Watch on YouTube" or copy link buttons
✅ Clean user experience
Based on recommended plyr-react implementation patterns.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implemented multiple overlay divs to prevent access to YouTube branding:
- Top overlay blocks "Watch on YouTube" button and title overlay
- Bottom-right overlay blocks YouTube logo
- Full-screen overlay with context menu prevention blocks right-click
- Set controls=0 parameter to hide YouTube controls completely
- Keep YouTube API working for time tracking and chapter navigation
This prevents users from:
- Clicking "Watch on YouTube" button
- Seeing YouTube logo
- Copying video URL via context menu
- Accessing YouTube native controls
While maintaining:
- Accurate time tracking via getCurrentTime()
- Chapter jump functionality via seekTo()
- Custom timeline with chapters
- Fullscreen capability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaces Plyr-based implementation with native YouTube IFrame Player API to fix:
- Accurate time tracking via getCurrentTime() polling every 500ms
- Chapter jump functionality using seekTo() and playVideo() API calls
- Right-click prevention with transparent overlay
- Proper chapter highlighting based on current playback time
Technical changes:
- Load YouTube IFrame API script on component mount
- Create YT.Player instance for programmatic control
- Poll getCurrentTime() in interval for real-time tracking
- Use getPlayerById() to retrieve player for jumpToTime operations
- Add pointer-events: none overlay with context menu prevention
- Generate unique iframe IDs for proper API targeting
This approach balances working timeline/jump functionality with preventing
direct URL access via overlay blocking right-click context menus.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Complete rewrite using YouTube's native iframe embed instead of Plyr + YouTube API.
This fixes all the bugs caused by API conflicts.
Changes:
- Remove Plyr library dependency
- Remove YouTube IFrame API script loading
- Use native YouTube iframe with enablejsapi=1
- Track time via postMessage events (infoDelivery)
- Jump to time via postMessage commands
- Remove all strategic overlays (not needed with native controls)
- Remove custom CSS for hiding YouTube elements
- Simple, clean iframe wrapper with 16:9 aspect ratio
- Enable YouTube's native controls (controls=1)
- Remove sandbox attribute that was blocking features
This approach:
✅ Fixes fullscreen permissions error
✅ Time tracking works reliably
✅ Chapter jump works via postMessage
✅ No overlays blocking controls
✅ Native YouTube controls available
✅ Much simpler code
The trade-off is that users can access YouTube UI, but this is better than
a broken player. The original goal of blocking YouTube UI is not achievable
without introducing significant bugs and complexity.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed issues:
1. Duration now displays correctly using Plyr's time tracking instead of YouTube API
2. Chapter jump functionality now works using Plyr's currentTime property
3. Overlays now properly avoid Plyr controls - only show when paused/ended
4. Hidden YouTube's native play button with CSS
Key changes:
- Use Plyr's native time tracking (player.currentTime) instead of YouTube API
- Jump to time uses Plyr's seek (player.currentTime = time, then play)
- Top overlay only appears when paused/ended (not when playing)
- Paused/ended overlays start at bottom: 80px to avoid Plyr controls
- Added CSS to hide .ytp-large-play-button and .html5-video-player background
- Moved time tracking to onReady callback to ensure player is initialized
- Removed dependencies on chapters from useEffect to prevent re-initialization
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Based on web research, implemented a comprehensive solution to prevent
users from accessing YouTube UI elements and copying/sharing video URLs.
Changes:
- Load YouTube IFrame Player API to track player state accurately
- Create YT Player instance to detect play/pause/end events
- Add strategic overlays to block specific YouTube UI elements:
- Top overlay (60px) - blocks "Copy Link" and title (always on)
- Bottom right overlay - blocks YouTube logo (always on)
- Bottom left overlay - blocks "Watch on YouTube" (before start)
- Center overlay - blocks "More Videos" (when paused)
- Large overlay - blocks related videos wall (when ended)
- Add sandbox attribute to iframe to prevent popups
- Track player state: -1=unstarted, 0=ended, 1=playing, 2=paused, 3=buffering, 5=cued
- All overlays prevent context menu and clicks with preventDefault
This approach is based on successful implementations found in:
- Medium article "How We Safely Embed YouTube Videos"
- xFanatical Safe Doc approach for educational content
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Key changes:
- Create .plyr__poster element manually (Plyr doesn't auto-create for YouTube)
- Set z-index: 10 and pointer-events: auto when visible
- Add 500ms delay before hiding poster on play (matches reference)
- Clear timeout if paused before poster fully hides
- poster blocks YouTube UI when paused/initial, fades out when playing
This matches the reference implementation behavior exactly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Instead of custom overlay, use Plyr's .plyr__poster element which:
- Naturally covers the iframe when paused/initial load
- Allows Plyr controls to receive clicks (z-index hierarchy)
- Hides (opacity: 0) when playing, shows (opacity: 1) when paused
- Blocks YouTube UI interactions when visible
This matches the reference implementation behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Track playback state and show overlay with pointer-events:auto when paused,
pointer-events:none when playing. This blocks YouTube UI (copy link, logo)
when video is paused or initially loaded, while allowing Plyr controls.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changed overlay to use pointer-events: none instead of pointer-events-auto.
This allows Plyr controls to receive clicks while still blocking YouTube iframe interactions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Update migration to use correct table name (platform_settings, not site_settings)
- Fix WebinarRecording.tsx to query platform_settings table
- Fix Bootcamp.tsx to query platform_settings table
- This allows authenticated users to access brand_accent_color for theming
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
## Changes
### Video Player CSS Overlay Fix
- Moved overlay inside plyr__video-embed container
- Added check for plyr__control--overlaid (play button)
- Now only blocks YouTube iframe, not Plyr controls
- Preserves full functionality of play button, progress bar, etc.
### Site Settings Permissions
- Added RLS policy to make site_settings publicly readable
- All authenticated users can now access brand_accent_color
- Fixes 404 error when members try to fetch accent color
## Technical Details
- Overlay now properly allows clicks through to Plyr controls
- Site settings now accessible via public RLS policy for authenticated users
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
## Changes
- Added max-height of 400px to chapter list container
- Added overflow-y-auto for vertical scrolling
- Added custom scrollbar styling (thin, with hover effects)
- Added pr-2 padding for scrollbar space
## Benefits
- Prevents timeline from becoming too tall for long videos
- Maintains consistent layout regardless of chapter count
- Better UX for 2-3+ hour videos with many chapters
- Smooth scrolling with styled scrollbar
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
## Changes
### Chapter Time Format Support
- Added HH:MM:SS format support in addition to MM:SS
- Updated time parsing to handle variable-length inputs
- Updated time display formatter to show hours when > 0
- Updated input placeholder and validation pattern
- Updated help text to mention both formats
### Video Player Improvements
- Added fullscreen button to Plyr controls
- Added quality/settings control to Plyr controls
- Fixed accent color theming with !important CSS rules
- Removed hardcoded default accent color (#f97316)
- Updated Bootcamp and WebinarRecording pages to use empty string initial state
### Access Control & Security
- Added transparent CSS overlay to block YouTube UI interactions
- Disabled YouTube native controls (controls=0, disablekb=1, fs=0)
- Set iframe pointer-events-none to prevent direct interaction
- Prevents members from copying/sharing YouTube URLs directly
- Preserves Plyr controls functionality through click handler
### Files Modified
- src/components/admin/ChaptersEditor.tsx: Time format HH:MM:SS support
- src/components/VideoPlayerWithChapters.tsx: Security overlay & theming fixes
- src/pages/Bootcamp.tsx: Accent color initialization fix
- src/pages/WebinarRecording.tsx: Accent color initialization fix
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implement timeline chapters for webinar and bootcamp videos with click-to-jump functionality:
**Components:**
- VideoPlayerWithChapters: Plyr.io-based player with chapter support
- TimelineChapters: Clickable chapter markers with active state
- ChaptersEditor: Admin UI for managing video chapters
**Features:**
- YouTube videos: Clickable timestamps that jump to specific time
- Embed videos: Static timeline display (non-clickable)
- Real-time chapter tracking during playback
- Admin-defined accent color for Plyr theme
- Auto-hides timeline when no chapters configured
**Database:**
- Add chapters JSONB column to products table (webinars)
- Add chapters JSONB column to bootcamp_lessons table
- Create indexes for faster queries
**Updated Pages:**
- WebinarRecording: Two-column layout (video + timeline)
- Bootcamp: Per-lesson chapter support
- AdminProducts: Chapter editor for webinars
- CurriculumEditor: Chapter editor for lessons
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed issues:
- Added isAvailable parameter to handleSlotClick to prevent clicks on unavailable slots
- Added validation to prevent selecting past dates in date picker
- Added validation in handlePreviousDay to block navigating to past dates
- Added warning message when trying to view past dates
- Prevents the "slot is not defined" error when clicking disabled slots
Now the modal properly:
- Blocks selecting dates before today
- Shows clear error messages for past dates
- Prevents clicks on unavailable time slots
- Handles edge cases like passed sessions being rescheduled
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhanced TimeSlotPickerModal with:
- Date selection via date input and navigation arrows
- Passed time slots filtered out (only show future slots for today)
- Complete schedule picker (date + time in one modal)
- Dynamic slot availability based on selected date
- Better UX with date/time sections clearly separated
Updated AdminConsulting to:
- Use editSessionDate when opening time slot picker
- Pass selected date back from modal to parent
- Handle date changes during rescheduling
Fixes the issue where admins could only select time slots for the original
session date, not the new rescheduled date.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>