import {NachklangFeedbackDB} from '../Feedback.db'; import {EventConfig, EventSummary, Question, Song} from '../feedback.interface'; /** * Returns all events currently eligible to receive feedback: * published, on or after their concert day, and before the deadline. */ export const getEligibleEvents = async (): Promise => { let conn = await NachklangFeedbackDB.getConnection(); try { const query = ` SELECT slug, name, subtitle, event_date, feedback_deadline, poster_image_url FROM events WHERE is_published = 1 AND event_date <= CURDATE() AND feedback_deadline >= NOW() ORDER BY event_date DESC`; const rows = await conn.query(query); return rows.map((row: any) => ({ slug: row.slug, name: row.name, subtitle: row.subtitle, eventDate: row.event_date, feedbackDeadline: row.feedback_deadline, posterImageUrl: row.poster_image_url })); } finally { await conn.end(); } }; export type EventLookupResult = | { status: 'OK'; eventId: number; event: EventConfig } | { status: 'NOT_FOUND' } | { status: 'CLOSED' }; /** * Resolves a slug to its full public config: meta, ordered setlist, ordered * active questions. Distinguishes "unknown slug" from "known but outside * its feedback window" so callers can respond 404 vs 410. Also used * internally by the submission flow, which additionally needs `eventId`. */ export const getEventConfigBySlug = async (slug: string): Promise => { let conn = await NachklangFeedbackDB.getConnection(); try { const eventQuery = ` SELECT event_id, slug, name, subtitle, event_date, feedback_deadline, intro_text, poster_image_url, is_published FROM events WHERE slug = ?`; const eventRows = await conn.query(eventQuery, [slug]); if (eventRows.length === 0) { return {status: 'NOT_FOUND'}; } const eventRow = eventRows[0]; const eligibleQuery = ` SELECT 1 FROM events WHERE event_id = ? AND is_published = 1 AND event_date <= CURDATE() AND feedback_deadline >= NOW()`; const eligibleRows = await conn.query(eligibleQuery, [eventRow.event_id]); if (eligibleRows.length === 0) { return {status: 'CLOSED'}; } const songsQuery = 'SELECT song_id, title, composer, position FROM songs WHERE event_id = ? ORDER BY position ASC'; const songRows = await conn.query(songsQuery, [eventRow.event_id]); const songs: Song[] = songRows.map((row: any) => ({ songId: row.song_id, title: row.title, composer: row.composer, position: row.position })); const questionsQuery = ` SELECT eq.event_question_id, eq.position, q.question_id, q.question_type, q.label, q.help_text FROM event_questions eq INNER JOIN questions q ON q.question_id = eq.question_id WHERE eq.event_id = ? AND eq.is_active = 1 ORDER BY eq.position ASC`; const questionRows = await conn.query(questionsQuery, [eventRow.event_id]); const questions: Question[] = questionRows.map((row: any) => ({ eventQuestionId: row.event_question_id, questionId: row.question_id, type: row.question_type, label: row.label, helpText: row.help_text, position: row.position })); return { status: 'OK', eventId: eventRow.event_id, event: { slug: eventRow.slug, name: eventRow.name, subtitle: eventRow.subtitle, eventDate: eventRow.event_date, feedbackDeadline: eventRow.feedback_deadline, posterImageUrl: eventRow.poster_image_url, introText: eventRow.intro_text, songs, questions } }; } finally { await conn.end(); } };