--- import AppLayout from '../../layouts/AppLayout.astro'; import Avatar from '../../components/Avatar.astro'; import { getDispatchWithPoll, getAdjacentDispatches, getPulseById, castOrChangeVote, recordActivity, countCabMembers, } from '../../lib/db'; import { parseDispatchSlug, dispatchSlug, dispatchKindLabel, dispatchKindPigment, roleLabel, } from '../../lib/format'; import { renderMd } from '../../lib/markdown'; const user = Astro.locals.user; const slugParam = Astro.params.slug ?? ''; const id = parseDispatchSlug(slugParam); if (!id) return Astro.redirect('/dispatches'); // Vote POST — handled before main render so we can refresh state if (Astro.request.method === 'POST') { const data = await Astro.request.formData(); const action = String(data.get('action') ?? ''); if (action === 'vote') { const pulseId = Number(data.get('pulse_id')); const optionIndex = Number(data.get('option_index')); const target = getPulseById(pulseId); if (target && target.status === 'open' && Number.isInteger(optionIndex) && optionIndex >= 0 && optionIndex < target.options.length) { const wasNew = castOrChangeVote(pulseId, user.id, optionIndex); if (wasNew) recordActivity(user.id, 'voted', 'pulse', pulseId); } return Astro.redirect(Astro.url.pathname); } } const d = getDispatchWithPoll(id, user.id); if (!d || d.status !== 'published') return Astro.redirect('/dispatches'); // Canonical-redirect when the slug changes after a rename — id is the authority const canonical = dispatchSlug(d); if (slugParam !== canonical) return Astro.redirect(`/dispatches/${canonical}`); const totalMembers = countCabMembers(); const { prev, next } = getAdjacentDispatches(d.id); function closeDayLabel(closesAt: string): string { const parsed = closesAt.includes('T') ? new Date(closesAt) : new Date(closesAt.replace(' ', 'T') + 'Z'); return new Intl.DateTimeFormat('en-GB', { weekday: 'long', timeZone: 'Europe/Copenhagen' }).format(parsed); } function parseUtc(s: string): Date { if (/T.*[Zz]$/.test(s) || /[+-]\d{2}:?\d{2}$/.test(s)) return new Date(s); return new Date(s.replace(' ', 'T') + 'Z'); } function fmt(iso: string): string { return new Intl.DateTimeFormat('en-GB', { day: 'numeric', month: 'long', year: 'numeric', timeZone: 'Europe/Copenhagen', }).format(parseUtc(iso)); } const bodyHtml = renderMd(d.body); ---
← All dispatches
{dispatchKindLabel(d.kind)}

{d.title}

{d.poll && ( )}