- Replaced Formik with react-hook-form in Logo, M3UGroupFilter, M3UProfile, Stream, StreamProfile, and UserAgent components.
- Integrated Yup for schema validation in all updated forms.
- Updated form submission logic to accommodate new form handling methods.
- Adjusted state management and error handling to align with react-hook-form's API.
- Ensured compatibility with existing functionality while improving code readability and maintainability.
Introduce gated logo loading system that ensures logos render after both
ChannelsTable and StreamsTable have completed their initial data fetch,
preventing visual race conditions and ensuring proper paint order.
Changes:
- Add `allowLogoRendering` flag to logos store to gate logo fetching
- Implement `onReady` callbacks in ChannelsTable and StreamsTable
- Add orchestration logic in Channels.jsx to coordinate table readiness
- Use double requestAnimationFrame to defer logo loading until after browser paint
- Remove background logo loading from App.jsx (now page-specific)
- Simplify fetchChannelAssignableLogos to reuse fetchAllLogos
- Remove logos dependency from ChannelsTable columns to prevent re-renders
This ensures visual loading order: Channels → EPG → Streams → Logos,
regardless of network speed or data size, without timer-based hacks.
- Row 1: Frequency, Day (if weekly), Hour, Minute, Period (if 12h)
- Row 2: Retention, Save button
- Use wrap=nowrap to keep time selectors on same row
- Set CrontabSchedule timezone to system timezone for accurate scheduling
- Replace time TextInput with hour/minute Select dropdowns for cleaner UX
- Remove UTC/local time conversion logic (handled by Celery)
- Add tests for timezone functionality in simple and advanced modes
Changed Created column from fixed size (160px) to flexible minSize (180px)
with whiteSpace: nowrap to ensure date/time displays on a single line.
The column can now expand as needed while maintaining readability of
timestamps in various date/time format combinations.
Added dynamic label to the Advanced (Cron Expression) switch to match
the Scheduled Backups toggle above it.
Now displays:
Scheduled Backups [Enabled]
Advanced (Cron Expression) [Enabled]
Provides consistent UI pattern and clearer status indication.
Changed Advanced (Cron Expression) from a labeled switch to a proper
Group with space-between layout matching the Scheduled Backups row above.
Now displays as:
Scheduled Backups [Enabled toggle]
Advanced (Cron Expression) [Toggle]
This creates consistent visual alignment with both text labels on the left
and toggle switches on the right.
Moved the Advanced (Cron Expression) switch outside the scheduleLoading
conditional and wrapped it in its own Group for proper alignment.
Layout is now:
- Scheduled Backups header with Enabled/Disabled switch
- Advanced (Cron Expression) toggle (aligned left)
- Schedule configuration inputs (conditional based on mode)
This provides clearer visual hierarchy and better UX.
- Import dayjs for date formatting
- Read date-format setting from localStorage ('mdy' or 'dmy')
- Move formatDate function into component to access user preferences
- Format dates according to user's date and time format settings:
- MDY: MM/DD/YYYY
- DMY: DD/MM/YYYY
- 12h: h:mm:ss A
- 24h: HH:mm:ss
The Created column now respects the same date/time format preferences
used throughout the app (Guide, Stats, DVR, SystemEvents, etc).
Updated regex to properly support step notation with asterisk (e.g., */2, */5).
Now supports all common cron patterns:
- * (wildcard)
- */2 (every 2 units - step notation)
- 5 (specific value)
- 1-5 (range)
- 1-5/2 (step within range)
- 1,3,5 (list)
- 10-20/5 (step within range)
Changed regex from:
/^(\*|(\d+(-\d+)?(,\d+(-\d+)?)*)(\/\d+)?)$/
To:
/^(\*\/\d+|\*|\d+(-\d+)?(\/\d+)?(,\d+(-\d+)?(\/\d+)?)*)$/
The key change is adding \*\/\d+ as the first alternative to explicitly
match step notation like */2, */5, */10, etc.
Backend already supports this via Django Celery Beat's CrontabSchedule,
which accepts standard cron syntax including step notation.
- Add validateCronExpression function with comprehensive validation:
- Checks for exactly 5 parts (minute hour day month weekday)
- Validates cron syntax (*, ranges, lists, steps)
- Validates numeric ranges (minute 0-59, hour 0-23, etc.)
- Returns detailed error messages for each validation failure
- Add cronError state to track validation errors
- Validate on input change with handleScheduleChange
- Display error message below input field
- Disable Save button when cron expression is invalid
- Auto-validate when switching to advanced mode
- Clear errors when switching back to simple mode
User gets immediate feedback on cron syntax errors before attempting to save.
Frontend changes:
- Add advanced mode toggle switch for cron expressions
- Show cron expression input with helpful examples when enabled
- Display format hints: "minute hour day month weekday"
- Provide common examples (daily, weekly, every 6 hours, etc.)
- Conditionally render simple or advanced scheduling UI
- Support switching between simple and advanced modes
Backend changes:
- Add cron_expression to schedule settings (SETTING_KEYS, DEFAULTS)
- Update get_schedule_settings to include cron_expression
- Update update_schedule_settings to handle cron_expression
- Extend _sync_periodic_task to parse and use cron expressions
- Parse 5-part cron format: minute hour day_of_month month_of_year day_of_week
- Create CrontabSchedule from cron expression or simple frequency
- Add validation and error handling for invalid cron expressions
This addresses maintainer feedback for "custom scheduler (cron style) for more control".
Users can now schedule backups with full cron flexibility beyond daily/weekly.
- Add timezone conversion functions (utcToLocal, localToUtc)
- Use user's configured timezone from Settings (localStorage 'time-zone')
- Convert times to UTC when saving to backend
- Convert times from UTC to local when loading from backend
- Display timezone info showing user's timezone and scheduled time
- Helper text shows: "Timezone: America/New_York • Backup will run at 03:00"
This addresses maintainer feedback to handle timezone properly:
backend stores/schedules in UTC, frontend displays/edits in user's local time.
- Import useWarningsStore from warnings store
- Add suppressWarning hook to component
- Add actionKey props to restore and delete confirmation dialogs
- Add onSuppressChange callback to enable "Don't ask again" functionality
This aligns BackupManager with the project's standard confirmation dialog pattern
used throughout the codebase (ChannelsTable, EPGsTable, etc).