chore: remove participants and calendar pages and nav links

This commit is contained in:
Jonathan 2026-04-19 20:51:55 +02:00
parent a0931ea527
commit d054b56bf7
4 changed files with 6 additions and 706 deletions

View file

@ -15,9 +15,7 @@ const navLinks = [
{ href: '/product', label: 'Product' }, { href: '/product', label: 'Product' },
{ href: '/updates', label: 'Updates' }, { href: '/updates', label: 'Updates' },
{ href: '/roadmap', label: 'Roadmap' }, { href: '/roadmap', label: 'Roadmap' },
{ href: '/calendar', label: 'Calendar' }, { href: '/contribute', label: 'Contribute'},
{ href: '/contribute', label: 'Contribute' },
{ href: '/participants', label: 'Participants' },
]; ];
const currentPath = Astro.url.pathname; const currentPath = Astro.url.pathname;

View file

@ -1,213 +0,0 @@
---
import { getCollection } from 'astro:content';
import AppLayout from '../layouts/AppLayout.astro';
import { fmtDate } from '../lib/markdown';
const user = Astro.locals.user;
const allMeetings = await getCollection('meetings');
const meetings = allMeetings.sort(
(a, b) => new Date(a.data.date).getTime() - new Date(b.data.date).getTime()
);
const now = new Date();
const upcoming = meetings.filter((m) => new Date(m.data.date) >= now);
const past = meetings.filter((m) => new Date(m.data.date) < now).reverse();
---
<AppLayout title="Calendar" user={user}>
<div class="page">
<header class="page-header">
<p class="label-sm eyebrow">Calendar</p>
<h1 class="display-md page-title">Meetings and sessions.</h1>
<p class="lead subtitle">
CAB sessions, demos, and reviews. Agendas posted two weeks before;
notes within 48 hours.
</p>
</header>
<div class="content">
{upcoming.length > 0 && (
<section class="meeting-section">
<h2 class="label-sm section-heading">Upcoming</h2>
<ul class="meeting-list">
{upcoming.map((meeting) => (
<li class="meeting-item">
<a href={`/calendar/${meeting.slug}`} class="meeting-link">
<div class="meeting-meta">
<time class="label-sm meeting-date" datetime={String(meeting.data.date)}>
{fmtDate(String(meeting.data.date))}
</time>
<span class="label-sm meeting-time">{meeting.data.time}</span>
</div>
<div class="meeting-info">
<h3 class="headline-sm meeting-title">{meeting.data.title}</h3>
<p class="body-sm meeting-location">{meeting.data.location}</p>
</div>
<span class="meeting-arrow" aria-hidden="true">↗</span>
</a>
</li>
))}
</ul>
</section>
)}
{past.length > 0 && (
<section class="meeting-section">
<h2 class="label-sm section-heading">Past</h2>
<ul class="meeting-list">
{past.map((meeting) => (
<li class="meeting-item past">
<a href={`/calendar/${meeting.slug}`} class="meeting-link">
<div class="meeting-meta">
<time class="label-sm meeting-date" datetime={String(meeting.data.date)}>
{fmtDate(String(meeting.data.date))}
</time>
<span class="label-sm meeting-time">{meeting.data.time}</span>
</div>
<div class="meeting-info">
<h3 class="headline-sm meeting-title">{meeting.data.title}</h3>
<p class="body-sm meeting-location">{meeting.data.location}</p>
</div>
<span class="meeting-arrow" aria-hidden="true">↗</span>
</a>
</li>
))}
</ul>
</section>
)}
</div>
</div>
</AppLayout>
<style>
.page {
padding: var(--space-12) var(--space-20) var(--space-16);
max-width: var(--content-max);
margin: 0 auto;
}
/* ── Header ──────────────────────────────────────────────────────── */
.page-header {
max-width: 44rem;
margin-bottom: var(--space-12);
}
.eyebrow {
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
color: var(--on-surface-muted);
margin-bottom: var(--space-3);
}
.page-title {
margin-bottom: var(--space-5);
}
.subtitle {
color: var(--on-surface-variant);
max-width: var(--reading-max);
margin: 0;
}
/* ── Content ─────────────────────────────────────────────────────── */
.content {
display: flex;
flex-direction: column;
gap: var(--space-12);
max-width: 52rem;
}
/* ── Section ─────────────────────────────────────────────────────── */
.section-heading {
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
color: var(--on-surface-muted);
margin-bottom: var(--space-4);
}
/* ── Meeting list ────────────────────────────────────────────────── */
.meeting-list {
list-style: none;
padding: 0;
margin: 0;
}
.meeting-item {
border-top: var(--ghost-border);
}
.meeting-item:last-child {
border-bottom: var(--ghost-border);
}
.meeting-link {
display: grid;
grid-template-columns: 10rem 1fr auto;
align-items: center;
gap: var(--space-5);
padding: var(--space-5) 0;
text-decoration: none;
border-bottom: none;
transition: background var(--duration-fast) var(--ease-standard);
}
.meeting-link:hover {
border-bottom: none;
}
.meeting-link:hover .meeting-title {
color: var(--secondary);
}
.meeting-link:hover .meeting-arrow {
color: var(--secondary);
}
.meeting-meta {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.meeting-date {
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--on-surface-variant);
}
.meeting-time {
color: var(--on-surface-muted);
letter-spacing: var(--tracking-normal);
}
.meeting-info {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.meeting-title {
font-family: var(--font-serif);
font-weight: 400;
letter-spacing: var(--tracking-snug);
margin: 0;
color: var(--on-surface);
transition: color var(--duration-fast) var(--ease-standard);
}
.meeting-location {
color: var(--on-surface-muted);
margin: 0;
}
.meeting-arrow {
font-family: var(--font-serif);
color: var(--on-surface-muted);
transition: color var(--duration-fast) var(--ease-standard);
}
.past .meeting-title {
color: var(--on-surface-variant);
}
.past .meeting-date {
color: var(--on-surface-muted);
}
</style>

View file

@ -1,300 +0,0 @@
---
import { getCollection, getEntry } from 'astro:content';
import AppLayout from '../../layouts/AppLayout.astro';
import { fmtDate, renderMd } from '../../lib/markdown';
import { getAttendanceSummary, getUserAttendance, setAttendance } from '../../lib/db';
const user = Astro.locals.user;
const { slug } = Astro.params;
const meeting = await getEntry('meetings', slug as string);
if (!meeting) return Astro.redirect('/calendar');
const isPast = new Date(meeting.data.date) < new Date();
// Handle attendance POST
if (Astro.request.method === 'POST') {
const data = await Astro.request.formData();
const status = data.get('status') as 'yes' | 'no' | null;
if (status === 'yes' || status === 'no') {
setAttendance(user.id, meeting.slug, status);
}
return Astro.redirect(`/calendar/${slug}`);
}
const { Content } = await meeting.render();
const attendance = getAttendanceSummary(meeting.slug);
const myStatus = getUserAttendance(user.id, meeting.slug);
---
<AppLayout title={meeting.data.title} user={user}>
<div class="page">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="/calendar" class="crumb label-sm">Calendar</a>
<span class="crumb-sep" aria-hidden="true"></span>
<span class="crumb label-sm crumb-current">{meeting.data.title}</span>
</nav>
<div class="layout">
<article class="article">
<header class="meeting-header">
<time class="label-sm meeting-date" datetime={String(meeting.data.date)}>
{fmtDate(String(meeting.data.date))}
</time>
<h1 class="display-md meeting-title">{meeting.data.title}</h1>
<dl class="meta-list">
<div class="meta-row">
<dt class="label-sm meta-label">Time</dt>
<dd class="body-md meta-value">{meeting.data.time}</dd>
</div>
<div class="meta-row">
<dt class="label-sm meta-label">Location</dt>
<dd class="body-md meta-value">{meeting.data.location}</dd>
</div>
{meeting.data.attendees && (
<div class="meta-row">
<dt class="label-sm meta-label">Attendees</dt>
<dd class="body-md meta-value">{meeting.data.attendees}</dd>
</div>
)}
</dl>
</header>
<div class="prose">
<Content />
</div>
</article>
{!isPast && (
<aside class="attendance-sidebar">
<div class="attendance-card">
<h2 class="label-sm attendance-heading">Will you attend?</h2>
<form method="POST" class="attendance-form">
<button
type="submit"
name="status"
value="yes"
class:list={['attend-btn', { active: myStatus === 'yes' }]}
>
Yes, I will be there
</button>
<button
type="submit"
name="status"
value="no"
class:list={['attend-btn attend-btn-no', { active: myStatus === 'no' }]}
>
No, I cannot make it
</button>
</form>
<div class="attendance-tally">
<span class="label-sm tally-yes">{attendance.yes} attending</span>
{attendance.no > 0 && (
<span class="label-sm tally-no">{attendance.no} not attending</span>
)}
</div>
</div>
</aside>
)}
</div>
</div>
</AppLayout>
<style>
.page {
padding: var(--space-10) var(--space-20) var(--space-16);
max-width: var(--content-max);
margin: 0 auto;
}
/* ── Breadcrumb ──────────────────────────────────────────────────── */
.breadcrumb {
display: flex;
align-items: center;
gap: var(--space-2);
margin-bottom: var(--space-8);
}
.crumb {
color: var(--on-surface-muted);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
text-decoration: none;
border-bottom: none;
transition: color var(--duration-fast) var(--ease-standard);
}
a.crumb:hover {
color: var(--on-surface-variant);
border-bottom: none;
}
.crumb-sep { color: var(--on-surface-muted); }
.crumb-current { color: var(--on-surface-variant); }
/* ── Layout ──────────────────────────────────────────────────────── */
.layout {
display: grid;
grid-template-columns: 1fr 18rem;
gap: var(--space-10);
align-items: start;
}
/* ── Article ─────────────────────────────────────────────────────── */
.meeting-header {
margin-bottom: var(--space-10);
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.meeting-date {
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--on-surface-muted);
}
.meeting-title {
margin: 0;
}
.meta-list {
display: flex;
flex-direction: column;
gap: var(--space-2);
margin: 0;
padding: 0;
}
.meta-row {
display: flex;
gap: var(--space-4);
align-items: baseline;
}
.meta-label {
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--on-surface-muted);
min-width: 5rem;
flex-shrink: 0;
}
.meta-value {
color: var(--on-surface-variant);
margin: 0;
}
/* ── Prose ───────────────────────────────────────────────────────── */
.prose :global(h2) {
font-family: var(--font-serif);
font-size: var(--text-headline-md);
font-weight: 400;
letter-spacing: var(--tracking-snug);
color: var(--on-surface);
margin: var(--space-8) 0 var(--space-4);
line-height: var(--leading-snug);
}
.prose :global(p) {
margin: 0 0 var(--space-4);
color: var(--on-surface-variant);
line-height: var(--leading-relaxed);
font-size: var(--text-body-lg);
}
.prose :global(ol),
.prose :global(ul) {
padding-left: var(--space-6);
margin: 0 0 var(--space-5);
color: var(--on-surface-variant);
line-height: var(--leading-relaxed);
font-size: var(--text-body-md);
}
.prose :global(li) {
margin-bottom: var(--space-2);
}
.prose :global(strong) {
font-weight: 600;
color: var(--on-surface);
}
.prose :global(em) {
font-style: italic;
color: var(--on-surface-muted);
}
/* ── Attendance sidebar ──────────────────────────────────────────── */
.attendance-card {
background: var(--surface-container-lowest);
border-radius: var(--radius-md);
padding: var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.attendance-heading {
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--on-surface-muted);
margin: 0;
}
.attendance-form {
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.attend-btn {
padding: var(--space-3) var(--space-4);
background: var(--surface-container-low);
border: var(--ghost-border);
border-radius: var(--radius-sm);
font-family: var(--font-sans);
font-size: var(--text-body-sm);
font-weight: 500;
color: var(--on-surface-variant);
cursor: pointer;
text-align: left;
transition: background var(--duration-fast) var(--ease-standard),
color var(--duration-fast) var(--ease-standard);
}
.attend-btn:hover {
background: var(--surface-container);
color: var(--on-surface);
}
.attend-btn.active {
background: var(--surface-container-high);
color: var(--on-surface);
font-weight: 600;
}
.attend-btn-no.active {
background: rgba(185, 107, 88, 0.08);
color: var(--pigment-terracotta);
}
.attendance-tally {
display: flex;
flex-direction: column;
gap: var(--space-1);
padding-top: var(--space-2);
border-top: var(--ghost-border);
}
.tally-yes {
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--pigment-copper);
}
.tally-no {
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--on-surface-muted);
}
</style>

View file

@ -1,185 +0,0 @@
---
import AppLayout from '../layouts/AppLayout.astro';
import { getAllUsersPublic } from '../lib/db';
import type { UserPublic, Role } from '../lib/db';
const user = Astro.locals.user;
const allUsers = getAllUsersPublic().filter((u) => u.active);
// Group by organisation
const orgs = new Map<string, UserPublic[]>();
for (const u of allUsers) {
if (!orgs.has(u.organisation)) orgs.set(u.organisation, []);
orgs.get(u.organisation)!.push(u);
}
const roleLabels: Record<Role, string> = {
pilot: 'Pilot',
cab: 'CAB',
fenja: 'Fenja',
};
const roleColors: Record<Role, string> = {
pilot: 'var(--pigment-copper)',
cab: 'var(--pigment-indigo)',
fenja: 'var(--secondary)',
};
---
<AppLayout title="Participants" user={user}>
<div class="page">
<header class="page-header">
<p class="label-sm eyebrow">Participants</p>
<h1 class="display-md page-title">The people.</h1>
<p class="lead subtitle">
Everyone in the Bifrost pilot — who they are, where they are from,
and what they bring.
</p>
</header>
<div class="orgs">
{[...orgs.entries()].map(([orgName, members]) => (
<section class="org-section">
<h2 class="headline-sm org-name">{orgName}</h2>
<ul class="member-list">
{members.map((member) => (
<li class="member-card">
<div class="member-header">
<span class="body-md member-name">{member.name}</span>
<span
class="role-badge label-sm"
style={`color: ${roleColors[member.role]}`}
>
{roleLabels[member.role]}
</span>
</div>
{member.bio && (
<p class="body-sm member-bio">{member.bio}</p>
)}
{member.id === user.id && (
<a href="/account" class="edit-bio-link label-sm">
Edit your bio
</a>
)}
</li>
))}
</ul>
</section>
))}
</div>
</div>
</AppLayout>
<style>
.page {
padding: var(--space-12) var(--space-20) var(--space-16);
max-width: var(--content-max);
margin: 0 auto;
}
/* ── Header ──────────────────────────────────────────────────────── */
.page-header {
max-width: 44rem;
margin-bottom: var(--space-12);
}
.eyebrow {
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
color: var(--on-surface-muted);
margin-bottom: var(--space-3);
}
.page-title { margin-bottom: var(--space-5); }
.subtitle {
color: var(--on-surface-variant);
max-width: var(--reading-max);
margin: 0;
}
/* ── Orgs ────────────────────────────────────────────────────────── */
.orgs {
display: flex;
flex-direction: column;
gap: var(--space-12);
max-width: 52rem;
}
.org-section {
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.org-name {
font-family: var(--font-serif);
font-weight: 400;
letter-spacing: var(--tracking-snug);
margin: 0;
padding-bottom: var(--space-4);
border-bottom: var(--ghost-border);
color: var(--on-surface);
}
/* ── Member cards ────────────────────────────────────────────────── */
.member-list {
list-style: none;
padding: 0;
margin: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
gap: var(--space-4);
}
.member-card {
background: var(--surface-container-lowest);
border-radius: var(--radius-md);
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.member-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: var(--space-3);
}
.member-name {
font-weight: 600;
color: var(--on-surface);
margin: 0;
}
.role-badge {
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
font-weight: 600;
flex-shrink: 0;
}
.member-bio {
color: var(--on-surface-variant);
margin: 0;
line-height: var(--leading-relaxed);
}
.edit-bio-link {
color: var(--on-surface-muted);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
text-decoration: none;
border-bottom: none;
transition: color var(--duration-fast) var(--ease-standard);
align-self: flex-start;
}
.edit-bio-link:hover {
color: var(--on-surface-variant);
border-bottom: none;
}
</style>