Implement hide breaks feature in AirSchedule component
ci / build-backend (push) Successful in 3m38s
ci / build-frontend (push) Successful in 38s
ci / tests (push) Successful in 1m27s
ci / sonar (push) Successful in 5m58s

Added a new state to manage the visibility of breaks in the AirSchedule component, allowing users to toggle the display of interstitials and advertisements. Updated the filtering logic to account for this new state, ensuring that the schedule reflects user preferences. Enhanced localization files to include new strings for the hide breaks functionality in both English and Russian.
This commit is contained in:
Leonid Pershin
2026-07-30 22:18:09 +03:00
parent de48317f7d
commit e983374dec
3 changed files with 38 additions and 15 deletions
@@ -57,6 +57,10 @@ export function AirSchedule({
// По умолчанию прошедшее скрыто: эфир смотрят, чтобы понять, что будет, а вчерашние страницы
// приходилось пролистывать каждый раз заново.
const [hidePast, setHidePast] = useState(true)
// Врезки тоже скрыты: между двумя сериями их бывает вчетверо больше, чем программ, и лента
// из реклам с заставками перестаёт читаться как программа передач. Снятая галочка показывает
// ленту как есть — ровно то, что уйдёт в эфир.
const [hideBreaks, setHideBreaks] = useState(true)
const range = useMemo(() => {
const from = dayStartUtc(day, utcOffsetMinutes, dayStartTime)
@@ -70,11 +74,15 @@ export function AirSchedule({
const entries = useMemo(() => data ?? [], [data])
const visible = useMemo(() => {
if (!hidePast) return entries
// Идущая запись остаётся: то, что сейчас в эфире, — ещё не история.
// Идущая запись остаётся: то, что сейчас в эфире, — ещё не история. Аварийный запас не врезка
// и не скрывается никогда: он и есть то, ради чего в эту ленту заглядывают.
const now = Date.now()
return entries.filter((entry) => new Date(entry.endsAtUtc).getTime() > now)
}, [entries, hidePast])
return entries.filter(
(entry) =>
(!hidePast || new Date(entry.endsAtUtc).getTime() > now) &&
(!hideBreaks || (entry.kind !== 'Ad' && entry.kind !== 'Bumper')),
)
}, [entries, hidePast, hideBreaks])
const totalPages = Math.max(1, Math.ceil(visible.length / PAGE_SIZE))
const pageItems = visible.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE)
@@ -106,17 +114,30 @@ export function AirSchedule({
)
})}
<label className="ml-auto flex items-center gap-2 text-xs text-muted-foreground">
<input
type="checkbox"
checked={hidePast}
onChange={(e) => {
setHidePast(e.target.checked)
setPage(1)
}}
/>
{t('admin.channels.airHidePast')}
</label>
<div className="ml-auto flex items-center gap-4 text-xs text-muted-foreground">
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={hidePast}
onChange={(e) => {
setHidePast(e.target.checked)
setPage(1)
}}
/>
{t('admin.channels.airHidePast')}
</label>
<label className="flex items-center gap-2">
<input
type="checkbox"
checked={hideBreaks}
onChange={(e) => {
setHideBreaks(e.target.checked)
setPage(1)
}}
/>
{t('admin.channels.airHideBreaks')}
</label>
</div>
</div>
{isLoading ? (
+1
View File
@@ -783,6 +783,7 @@ export const en = {
airToday: 'Today',
airCount: 'Entries for the day: {{count}}',
airHidePast: 'Hide past entries',
airHideBreaks: 'Hide breaks',
},
junctions: {
title: 'Junctions',
+1
View File
@@ -778,6 +778,7 @@ export const ru = {
airToday: 'Сегодня',
airCount: 'Записей за сутки: {{count}}',
airHidePast: 'Скрывать прошедшее',
airHideBreaks: 'Скрывать врезки',
},
junctions: {
title: 'Стыки',