Initial commit

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-05-11 15:55:14 -04:00
commit 290ff0bc1e
41 changed files with 7998 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import ContactList from '../components/ContactList.astro';
import { getContacts } from '../lib/directus';
import { site } from '../lib/site';
const contacts = await getContacts();
---
<BaseLayout title="Contact" description="How to reach Swansea Airport and its public contacts.">
<div class="container stack">
<section class="prose">
<p class="eyebrow">Contact</p>
<h1 class="section-title">Reach the airport team</h1>
<p>{site.address}</p>
<p><a href={`tel:${site.phone.replace(/\s+/g, '')}`}>{site.phone}</a></p>
</section>
<ContactList contacts={contacts} />
</div>
</BaseLayout>
+13
View File
@@ -0,0 +1,13 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import DocumentList from '../components/DocumentList.astro';
import { getDocuments } from '../lib/directus';
const documents = await getDocuments();
---
<BaseLayout title="Documents" description="Airport documents and downloads for pilots, visitors, and staff.">
<div class="container">
<DocumentList documents={documents} />
</div>
</BaseLayout>
+24
View File
@@ -0,0 +1,24 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import { getEvents } from '../../lib/directus';
import { formatDateTime } from '../../lib/format';
type EventItem = Awaited<ReturnType<typeof getEvents>>[number];
export async function getStaticPaths() {
const events = await getEvents();
return events.map((item) => ({ params: { slug: item.slug }, props: { item } }));
}
const { item } = Astro.props as { item: EventItem };
---
<BaseLayout title={item.title} description={item.description}>
<article class="container prose">
<p class="meta">{formatDateTime(item.start_datetime)}</p>
<h1 class="section-title">{item.title}</h1>
<p>{item.description}</p>
{item.location_text && <p><strong>Location:</strong> {item.location_text}</p>}
{item.registration_link && <p><a class="button primary" href={item.registration_link}>Register</a></p>}
</article>
</BaseLayout>
+13
View File
@@ -0,0 +1,13 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import EventsList from '../../components/EventsList.astro';
import { getEvents } from '../../lib/directus';
const events = (await getEvents()).sort((left, right) => new Date(left.start_datetime).getTime() - new Date(right.start_datetime).getTime());
---
<BaseLayout title="Events" description="Airport events and flying opportunities.">
<div class="container">
<EventsList events={events} title="Events listing" description="Scannable listings for public and operational events." />
</div>
</BaseLayout>
+80
View File
@@ -0,0 +1,80 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import NoticeBanner from '../components/NoticeBanner.astro';
import FuelPricesWidget from '../components/FuelPricesWidget.astro';
import EventsList from '../components/EventsList.astro';
import NewsFeed from '../components/NewsFeed.astro';
import { homepageHighlights, site } from '../lib/site';
import { getEvents, getFuelPrices, getNews, getNotices } from '../lib/directus';
const [notices, fuelPrices, events, news] = await Promise.all([
getNotices(),
getFuelPrices(),
getEvents(),
getNews(),
]);
const featuredEvents = events.filter((event) => event.is_featured).slice(0, 3);
const latestNews = news.slice(0, 3);
---
<BaseLayout title="Home" description="Fast, clear airfield information for pilots and visitors.">
<section class="hero">
<div class="container hero-grid">
<div class="hero-panel">
<p class="eyebrow">Operational website</p>
<h1 class="hero-title">Clear airfield information, built for speed.</h1>
<p class="hero-copy">
Visitor-critical information stays visible up front, while Directus supplies notices, news, events, and fuel pricing at build time.
</p>
<div class="cta-row">
<a class="button primary" href="/visiting-pilots/">Visiting pilots</a>
<a class="button secondary" href="/procedures-safety-noise-abatement/">Procedures and safety</a>
</div>
</div>
<aside class="hero-rail">
<div class="surface">
<p class="eyebrow">Today at the airfield</p>
<div class="stats-grid">
<div class="stat">
<strong>{site.openingHours}</strong>
<span class="muted">Opening hours</span>
</div>
<div class="stat">
<strong>{site.licensedHours}</strong>
<span class="muted">Licensed hours</span>
</div>
</div>
</div>
<div class="surface">
<p class="eyebrow">Runway overview</p>
<ul class="compact-list">
{site.runwayFacts.map((fact) => (
<li>{fact}</li>
))}
</ul>
</div>
</aside>
</div>
</section>
<div class="container stack">
<NoticeBanner notices={notices} />
<FuelPricesWidget fuelPrices={fuelPrices} />
<section>
<div class="cards-grid">
{homepageHighlights.map((item) => (
<article class="card">
<h3>{item.title}</h3>
<p>{item.body}</p>
</article>
))}
</div>
</section>
<EventsList events={featuredEvents} title="Upcoming events" description="Featured events are surfaced here first for quick scanning." />
<NewsFeed news={latestNews} />
</div>
</BaseLayout>
+23
View File
@@ -0,0 +1,23 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import { getNews } from '../../lib/directus';
import { formatDate } from '../../lib/format';
type NewsItem = Awaited<ReturnType<typeof getNews>>[number];
export async function getStaticPaths() {
const news = await getNews();
return news.map((item) => ({ params: { slug: item.slug }, props: { item } }));
}
const { item } = Astro.props as { item: NewsItem };
---
<BaseLayout title={item.title} description={item.summary}>
<article class="container prose">
<p class="meta">Published {formatDate(item.publish_date)}</p>
<h1 class="section-title">{item.title}</h1>
<p>{item.summary}</p>
<div set:html={item.body} />
</article>
</BaseLayout>
+13
View File
@@ -0,0 +1,13 @@
---
import BaseLayout from '../../layouts/BaseLayout.astro';
import NewsFeed from '../../components/NewsFeed.astro';
import { getNews } from '../../lib/directus';
const news = (await getNews()).sort((left, right) => new Date(right.publish_date).getTime() - new Date(left.publish_date).getTime());
---
<BaseLayout title="News" description="Latest airport news and operational updates.">
<div class="container">
<NewsFeed news={news} title="All news" description="Read the latest public updates and operational announcements." />
</div>
</BaseLayout>
@@ -0,0 +1,29 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="Procedures, Safety and Noise Abatement" description="Operational procedures, safety notes, and noise-sensitive guidance.">
<div class="container prose">
<p class="eyebrow">Operations</p>
<h1 class="section-title">Procedures, safety, and noise abatement</h1>
<p>
This page is intentionally text-led and easy to scan. It is controlled by Astro so the structure stays stable even as the content evolves.
</p>
<h2>Safety priorities</h2>
<div class="cards-grid">
<article class="card">
<h3>Brief before flight</h3>
<p>Surface the checklist items pilots need most, without burying them under visual clutter.</p>
</article>
<article class="card">
<h3>Check current notices</h3>
<p>Operational notices should be reviewed before taxi, because the homepage is fed by the same notices collection.</p>
</article>
<article class="card">
<h3>Respect local noise guidance</h3>
<p>Noise abatement text can be expanded in Directus while the page structure stays fixed in code.</p>
</article>
</div>
</div>
</BaseLayout>
+35
View File
@@ -0,0 +1,35 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import { site } from '../lib/site';
---
<BaseLayout title="Visiting Pilots" description="Essential information for pilots planning a visit to Swansea Airport.">
<div class="container prose">
<p class="eyebrow">Visiting pilots</p>
<h1 class="section-title">Essential information for arriving aircraft</h1>
<p>
This page is structured for quick pre-flight checks. It keeps operational details in fixed Astro components and leaves content updates to Directus.
</p>
<h2>Airport facts</h2>
<ul>
{site.runwayFacts.map((fact) => <li>{fact}</li>)}
</ul>
<h2>Arrival essentials</h2>
<div class="cards-grid">
<article class="card">
<h3>PPR</h3>
<p>Pre-landing fogging is presented prominently here and can be linked to the relevant Directus content or booking workflow.</p>
</article>
<article class="card">
<h3>Book out</h3>
<p>Departure procedures and any required outbound reporting remain in the same controlled page structure.</p>
</article>
<article class="card">
<h3>Fuel and services</h3>
<p>Fuel prices are shown on the homepage and can be reused here with the same data source.</p>
</article>
</div>
</div>
</BaseLayout>