chore: remove participants and calendar pages and nav links
This commit is contained in:
parent
a0931ea527
commit
d054b56bf7
4 changed files with 6 additions and 706 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -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>
|
|
||||||
Loading…
Add table
Reference in a new issue