Last active
March 1, 2025 23:02
-
-
Save jahirfiquitiva/215faf075ec057f473d25fe88882e708 to your computer and use it in GitHub Desktop.
Community events Astro site config
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1. Import utilities from `astro:content` | |
import { defineCollection, z } from "astro:content"; | |
// 2. Import loader(s) | |
import { glob, file, type Loader, type LoaderContext } from "astro/loaders"; | |
// 3. Define your collection(s) | |
import fs from "node:fs/promises"; | |
import path from "node:path"; | |
import matter from "gray-matter"; | |
// Define schemas | |
const speakerSchema = z.object({ | |
id: z.string(), | |
name: z.string(), | |
description: z.string().optional(), | |
company: z.string(), | |
position: z.string(), | |
website: z.string().url().optional(), | |
photo: z.string(), | |
social: z | |
.object({ | |
twitter: z.string().optional(), | |
github: z.string().optional(), | |
instagram: z.string().optional(), | |
linkedin: z.string().optional(), | |
bluesky: z.string().optional(), | |
mastodon: z.string().optional(), | |
threads: z.string().optional(), | |
}) | |
.optional(), | |
}); | |
const sponsorSchema = z.object({ | |
id: z.string(), | |
name: z.string(), | |
logo: z.string(), | |
website: z.string().url().optional(), | |
description: z.string().optional(), | |
}); | |
const sponsorCategoryEnum = z.enum(["gold", "emerald", "silver", "community"]); | |
const sponsorsSchema = z.record(sponsorCategoryEnum, z.array(sponsorSchema)); | |
const agendaItemSchema = z.object({ | |
time: z.string(), | |
speaker: z.string(), | |
title: z.string(), | |
pitch: z.string(), | |
recordingUrl: z.string().url().optional(), | |
}); | |
const agendaSchema = z.record(z.string(), z.array(agendaItemSchema)); | |
const venueSchema = z.object({ | |
title: z.string().optional(), | |
address: z.string().optional(), | |
city: z.string().optional(), | |
capacity: z.number().optional(), | |
}); | |
// Base path for event data | |
const DATA_BASE_PATH = "src/data"; | |
const eventEntrySchema = z.object({ | |
speakers: z.array(speakerSchema).default([]), | |
sponsors: sponsorsSchema.default({}), | |
agenda: agendaSchema, | |
venue: z.object({ | |
data: venueSchema, | |
content: z.string() | |
}).optional(), | |
}); | |
// Helper function to load a JSON file | |
async function loadJsonFile(filePath: string) { | |
try { | |
const content = await fs.readFile(filePath, "utf-8"); | |
return JSON.parse(content); | |
} catch (err) { | |
return null; | |
} | |
} | |
// Helper function to load a markdown file | |
async function loadMarkdownFile(filePath: string) { | |
try { | |
const content = await fs.readFile(filePath, "utf-8"); | |
return matter(content); | |
} catch (err) { | |
return null; | |
} | |
} | |
type EventEntry = z.infer<typeof eventEntrySchema>; | |
// Helper function to process a directory and generate entries | |
async function processDirectory(dirPath: string, idPrefix: string = "") { | |
const entry: EventEntry = { | |
speakers: [], | |
sponsors: {}, | |
agenda: {}, | |
}; | |
// Load speakers.json | |
const speakersData = await loadJsonFile(path.join(dirPath,'/', "speakers.json")); | |
if (speakersData) { | |
entry.speakers = speakersData || []; | |
} | |
// Load sponsors.json | |
const sponsorsData = await loadJsonFile(path.join(dirPath,'/', "sponsors.json")); | |
if (sponsorsData) { | |
entry.sponsors = sponsorsData || {}; | |
} | |
// Load agenda.json | |
const agendaData = await loadJsonFile(path.join(dirPath,'/', "agenda.json")); | |
if (agendaData) { | |
entry.agenda = agendaData || {}; | |
} | |
// Load venue.md | |
const venueData = await loadMarkdownFile(path.join(dirPath,'/', "venue.md")); | |
if (venueData) { | |
entry.venue = venueData; | |
} | |
return { | |
id: idPrefix, | |
...entry, | |
}; | |
} | |
// Create a generic loader factory | |
function createEventLoader(eventType: "conf" | "express" | "meetup", hasMonths = false) { | |
return async () => { | |
const entries = []; | |
const typePath = path.join(DATA_BASE_PATH, eventType); | |
try { | |
const years = await fs.readdir(typePath); | |
for (const year of years) { | |
const yearPath = path.join(typePath, year); | |
const yearStat = await fs.stat(yearPath); | |
if (!yearStat.isDirectory()) { | |
continue; | |
} | |
if (!hasMonths) { | |
// For conf events, load directly from year directory | |
const yearEntry = await processDirectory(yearPath, `${year}`); | |
entries.push(yearEntry); | |
} else { | |
// For express and meetup events, load from year/month directories | |
try { | |
const months = await fs.readdir(yearPath); | |
for (const month of months) { | |
const monthPath = path.join(yearPath, month); | |
const monthStat = await fs.stat(monthPath); | |
if (monthStat.isDirectory()) { | |
const monthEntry = await processDirectory(monthPath, `${year}/${month}`); | |
entries.push(monthEntry); | |
} | |
} | |
} catch (err) { | |
// Year directory might not contain months or be readable, continue | |
} | |
} | |
} | |
} catch (err) { | |
// Type directory might not exist, continue | |
} | |
return entries; | |
}; | |
} | |
// Define collection loaders using the factory | |
const confLoader = createEventLoader("conf", false); | |
const expressLoader = createEventLoader("express", true); | |
const meetupLoader = createEventLoader("meetup", true); | |
const eventCollectionSchema = eventEntrySchema.extend({ | |
id: z.string(), | |
}); | |
// Define collections | |
export const collections = { | |
conf: defineCollection({ | |
schema: eventCollectionSchema, | |
loader: confLoader, | |
}), | |
express: defineCollection({ | |
schema: eventCollectionSchema, | |
loader: expressLoader, | |
}), | |
meetup: defineCollection({ | |
schema: eventCollectionSchema, | |
loader: meetupLoader, | |
}), | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
import Layout from "../layouts/layout.astro"; | |
import { getCollection } from "astro:content"; | |
const allConfs = await getCollection("conf"); | |
const allExpress = await getCollection("express"); | |
const allMeetups = await getCollection("meetup"); | |
--- | |
<Layout> | |
<section> | |
<h1>Conferences</h1> | |
<ul> | |
{ | |
allConfs.map((conf) => ( | |
<li> | |
<div> | |
<h2>{conf.id}</h2> | |
<h3>Speakers</h3> | |
<ul> | |
{conf.data.speakers.map((sp) => ( | |
<li>{sp.name}</li> | |
))} | |
</ul> | |
<h3>Sponsors</h3> | |
<ul> | |
{Object.keys(conf.data.sponsors).map((category) => ( | |
<li> | |
<div> | |
<h4>{category}</h4> | |
<ul> | |
{conf.data.sponsors[ | |
category as keyof typeof conf.data.sponsors | |
]?.map((sponsor) => ( | |
<li>{sponsor.name}</li> | |
))} | |
</ul> | |
</div> | |
</li> | |
))} | |
</ul> | |
</div> | |
</li> | |
)) | |
} | |
</ul> | |
</section> | |
<section> | |
<h1>Express</h1> | |
<ul> | |
{ | |
allExpress.map((conf) => ( | |
<li> | |
<div> | |
<h2>{conf.id}</h2> | |
<h3>Speakers</h3> | |
<ul> | |
{conf.data.speakers.map((sp) => ( | |
<li>{sp.name}</li> | |
))} | |
</ul> | |
<h3>Sponsors</h3> | |
<ul> | |
{Object.keys(conf.data.sponsors).map((category) => ( | |
<li> | |
<div> | |
<h4>{category}</h4> | |
<ul> | |
{conf.data.sponsors[ | |
category as keyof typeof conf.data.sponsors | |
]?.map((sponsor) => ( | |
<li>{sponsor.name}</li> | |
))} | |
</ul> | |
</div> | |
</li> | |
))} | |
</ul> | |
</div> | |
</li> | |
)) | |
} | |
</ul> | |
</section> | |
<section> | |
<h1>Meetups</h1> | |
<ul> | |
{ | |
allMeetups.map((conf) => ( | |
<li> | |
<div> | |
<h2>{conf.id}</h2> | |
<h3>Speakers</h3> | |
<ul> | |
{conf.data.speakers.map((sp) => ( | |
<li>{sp.name}</li> | |
))} | |
</ul> | |
<h3>Sponsors</h3> | |
<ul> | |
{Object.keys(conf.data.sponsors).map((category) => ( | |
<li> | |
<div> | |
<h4>{category}</h4> | |
<ul> | |
{conf.data.sponsors[ | |
category as keyof typeof conf.data.sponsors | |
]?.map((sponsor) => ( | |
<li>{sponsor.name}</li> | |
))} | |
</ul> | |
</div> | |
</li> | |
))} | |
</ul> | |
</div> | |
</li> | |
)) | |
} | |
</ul> | |
</section> | |
</Layout> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"id": "fulanito", | |
"name": "Fulanito", | |
"company": "Company Co.", | |
"position":"Developer", | |
"photo": "./img/speakers/fulanito.png" | |
} | |
] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"gold": [{ | |
"id":"company", | |
"logo": "./img/sponsors/company.png", | |
"name": "Company" | |
}], | |
"emerald": [{ | |
"id":"company", | |
"logo": "./img/sponsors/company.png", | |
"name": "Company" | |
}] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment