Implement schedule WYSIWYG and UTC dates

This commit is contained in:
2026-08-02 14:04:41 +01:00
parent a9d1d45d78
commit 2b9cabdab2
31 changed files with 1585 additions and 52 deletions
+7
View File
@@ -4,6 +4,7 @@ const { fetchAdminData, fetchPlaylistsPage, fetchSlidesPage, fetchTemplatesPage,
const { ANNOUNCEMENT_TYPES, ANNOUNCEMENT_COLORS, ANNOUNCEMENT_ICONS, DEFAULT_ANNOUNCEMENT_ICON, normalizeAnnouncementType, normalizeAnnouncementColor, normalizeAnnouncementIcon, fetchAnnouncementsPage, fetchAnnouncementById, fetchActiveAnnouncement, buildAnnouncementPayload } = require('./announcements');
const { ANNOUNCEMENT_ICON_OPTIONS, ANNOUNCEMENT_ICON_LABELS } = require('./announcement-icons');
const { fetchPlaylistById } = require('./playlists');
const { normalizeDisplayMode, fetchSchedulesData, fetchScheduleGroupsPage, fetchScheduleGroupById, fetchScheduleEntriesByGroupId, buildScheduleGroupPayload } = require('./schedules');
const { fetchApiSourcesData, fetchApiSourcesPage, fetchApiSourceById, fetchApiSourceResponse, buildApiSourcePayload } = require('./api-sources');
const { fetchRssFeedsData, fetchRssFeedsPage, fetchRssFeedById, fetchRssFeedItemsByFeedId, normalizeRssFeedItem, buildRssFeedPayload, fetchRssFeedItems, replaceRssFeedItems } = require('./rss-feeds');
const { slugify, uniqueScreenSlug, fetchScreenById, fetchScreenEditData, fetchScreenPlayerUrls, fetchScreenPlayerRecord } = require('./screens');
@@ -36,6 +37,12 @@ module.exports = {
fetchActiveAnnouncement,
buildAnnouncementPayload,
fetchPlaylistById,
normalizeDisplayMode,
fetchSchedulesData,
fetchScheduleGroupsPage,
fetchScheduleGroupById,
fetchScheduleEntriesByGroupId,
buildScheduleGroupPayload,
fetchApiSourcesData,
fetchApiSourcesPage,
fetchApiSourceById,
+120
View File
@@ -0,0 +1,120 @@
// Schedule group and entry data access helpers.
const { fetchPagedRows } = require('./utils');
function normalizeDisplayMode(value) {
const mode = String(value || 'upcoming').trim().toLowerCase();
if (mode === 'current' || mode === 'both') {
return mode;
}
return 'upcoming';
}
async function fetchSchedulesData(pool) {
const [scheduleGroups] = await pool.query(`
SELECT g.id, g.name, g.short_description, g.created_at, g.modified_at, g.created_by, g.modified_by,
(SELECT COUNT(*) FROM i_schedule_entries e WHERE e.schedule_group_id = g.id) AS entry_count,
(SELECT MIN(e.start_datetime) FROM i_schedule_entries e WHERE e.schedule_group_id = g.id AND e.start_datetime IS NOT NULL) AS next_start_datetime
FROM i_schedule_groups g
ORDER BY g.modified_at DESC, g.id DESC
`);
const [scheduleEntries] = await pool.query(`
SELECT id, schedule_group_id, title, short_description, start_datetime, end_datetime, created_at, modified_at, created_by, modified_by
FROM i_schedule_entries
ORDER BY schedule_group_id ASC, start_datetime ASC, id ASC
`);
const entriesByGroupId = new Map();
scheduleEntries.forEach(function (entry) {
const groupId = Number(entry.schedule_group_id);
if (!entriesByGroupId.has(groupId)) {
entriesByGroupId.set(groupId, []);
}
entriesByGroupId.get(groupId).push(entry);
});
const groups = scheduleGroups.map(function (group) {
return Object.assign({}, group, {
entries: entriesByGroupId.get(Number(group.id)) || []
});
});
return {
scheduleGroups: groups,
scheduleEntries: scheduleEntries
};
}
async function fetchScheduleGroupsPage(pool, page, pageSize, searchTerm, sortKey, sortDirection) {
const paged = await fetchPagedRows(pool, {
selectSql: `SELECT g.id, g.name, g.short_description, g.created_at, g.modified_at, g.created_by, g.modified_by,
(SELECT COUNT(*) FROM i_schedule_entries e WHERE e.schedule_group_id = g.id) AS entry_count,
(SELECT MIN(e.start_datetime) FROM i_schedule_entries e WHERE e.schedule_group_id = g.id AND e.start_datetime IS NOT NULL) AS next_start_datetime
FROM i_schedule_groups g
ORDER BY g.modified_at DESC, g.id DESC`,
countSql: 'SELECT COUNT(*) AS count FROM i_schedule_groups',
searchColumns: ['g.name', 'g.short_description'],
searchTerm: searchTerm,
sortColumns: {
name: 'g.name',
description: 'g.short_description',
entries: 'entry_count',
next_start: 'next_start_datetime',
created: 'g.created_at',
modified: 'g.modified_at'
},
sortKey: sortKey,
sortDirection: sortDirection,
page: page,
pageSize: pageSize
});
return Object.assign({ scheduleGroups: paged.rows }, paged);
}
async function fetchScheduleGroupById(pool, id) {
const [rows] = await pool.query(
'SELECT id, name, short_description, created_at, modified_at, created_by, modified_by FROM i_schedule_groups WHERE id = ?',
[id]
);
return rows[0] || null;
}
async function fetchScheduleEntriesByGroupId(pool, scheduleGroupId) {
const [rows] = await pool.query(
`SELECT id, schedule_group_id, title, short_description, start_datetime, end_datetime, created_at, modified_at, created_by, modified_by
FROM i_schedule_entries
WHERE schedule_group_id = ?
ORDER BY start_datetime ASC, id ASC`,
[scheduleGroupId]
);
return rows;
}
function buildScheduleGroupPayload(req, existingScheduleGroup) {
const fallback = existingScheduleGroup || {};
const name = String(req.body.name || fallback.name || '').trim();
const shortDescription = String(req.body.short_description || req.body.shortDescription || fallback.short_description || '').trim();
if (!name) {
const error = new Error('Schedule group name is required.');
error.statusCode = 400;
throw error;
}
return {
name: name,
shortDescription: shortDescription
};
}
module.exports = {
normalizeDisplayMode,
fetchSchedulesData,
fetchScheduleGroupsPage,
fetchScheduleGroupById,
fetchScheduleEntriesByGroupId,
buildScheduleGroupPayload
};