Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .astro/collections/meetups.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
"minLength": 10
},
"cover": {
"type": "string",
"format": "uri"
"type": "string"
},
"location": {
"type": "object",
Expand Down Expand Up @@ -231,8 +230,7 @@
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri"
"type": "string"
},
"type": {
"default": "image",
Expand Down
2 changes: 1 addition & 1 deletion .astro/data-store.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .astro/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_variables": {
"lastUpdateCheck": 1780140273670
"lastUpdateCheck": 1781261337506
}
}
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ pnpm-debug.log*

# jetbrains setting folder
.idea/

# Generated by local agent tooling
.codebase-indexer/
.sentinel/
CLAUDE.md
3 changes: 2 additions & 1 deletion content/meetups/100-january-2026.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ description: >
cover: https://pbs.twimg.com/media/G-tCqAnWIAA3ny4?format=jpg&name=large

location:
label: buro.gh, Accra
name: buro.gh, Accra
map_url: https://maps.app.goo.gl/n8u6C6TgdtW35db67
url: https://maps.app.goo.gl/n8u6C6TgdtW35db67

stream_url: null
registration_url: https://luma.com/70danu1w
Expand Down
3 changes: 2 additions & 1 deletion content/meetups/200-february-2026.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ description: >
cover: https://pbs.twimg.com/media/HAGkdjbWEAAJFba?format=jpg&name=large

location:
label: Nyansa Square, Accra
name: Nyansa Square.
map_url: https://maps.app.goo.gl/NbaFFBcy89Dehq8VA
url: https://maps.app.goo.gl/NbaFFBcy89Dehq8VA

stream_url: null
registration_url: https://luma.com/frh11t0y
Expand Down
3 changes: 2 additions & 1 deletion content/meetups/300-march-2026.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ description: >
cover: https://pbs.twimg.com/media/HCaAt7HWsAAWOcT?format=jpg&name=large

location:
label: Fido, Accra
name: Fido, Opposite SSNIT Guest House, Julius Nyerere Rd, Accra
map_url: https://www.google.com/maps/search/?api=1&query=5.570918%2C-0.18906119999999998
url: https://www.google.com/maps/search/?api=1&query=5.570918%2C-0.18906119999999998

stream_url: null
registration_url: https://luma.com/jf8pjncl
Expand Down
3 changes: 2 additions & 1 deletion content/meetups/400-april-2026.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ description: >
cover: https://pbs.twimg.com/media/HCLwbPBXYAAzGdM?format=jpg&name=large

location:
label: Fido, Accra
name: Fido, Opposite SSNIT Guest House, Julius Nyerere Rd, Accra
map_url: https://www.google.com/maps/search/?api=1&query=5.570918%2C-0.18906119999999998
url: https://www.google.com/maps/search/?api=1&query=5.570918%2C-0.18906119999999998

stream_url: null
registration_url: https://lu.ma/qqx9uk6u
Expand Down
3 changes: 2 additions & 1 deletion content/meetups/500-may-2026.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ description: >
cover: https://pbs.twimg.com/media/HG_xUr-WIAABl-e?format=jpg&name=medium

location:
label: buro.gh, Accra
name: buro.gh, Accra
map_url: https://maps.app.goo.gl/n8u6C6TgdtW35db67
url: https://maps.app.goo.gl/n8u6C6TgdtW35db67

stream_url: https://x.com/i/broadcasts/1aKbddMWOjaJX
registration_url: https://lu.ma/3b0il40a
Expand Down
56 changes: 56 additions & 0 deletions content/meetups/600-april-2026.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: DevCongress April Quarterly Meetup
start: "2026-04-04T18:00:00+00:00"
end: "2026-04-04T21:45:00+00:00"
description: >
An informal online quarterly meetup for DevCongress members in Ghana and
across the diaspora. The April conversation centered on AI, coding, community
feedback, and how we can keep monthly meetups funded and useful.
cover: /images/quarterly-april-meet-up.jpeg

location:
label: Google Meet
name: Google Meet
url: null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wont block this. But Dede has been given access to the meet. We can add it as a add to your calendar.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a past event(I can add the meeting link)...but for the future ones, should we change from register to add to your calendar for the g-meet events?


stream_url: null
registration_url: null

speakers: []

schedule:
- time: "6:00 PM"
title: Greetings, weather check & community banter
type: networking
lead: null
resources: []
- time: "6:30 PM"
title: What's happening in the tech ecosystem
type: open_discussion
lead: null
resources: []
- time: "6:45 PM"
title: "Open discussion: AI, coding, hallucinations & Leetcode for AI"
type: open_discussion
lead: null
resources: []
- time: "8:00 PM"
title: "Community feedback: what DevCongress can improve"
type: open_discussion
lead: null
resources: []
- time: "8:45 PM"
title: Funding monthly meetups and community support
type: open_discussion
lead: null
resources: []
- time: "9:30 PM"
title: Open banter & close
type: networking
lead: null
resources: []

photos:
- url: /images/quarterly-april-meet-up.jpeg
- url: /images/quarterly-april-meetup-2.jpeg

videos: []
69 changes: 69 additions & 0 deletions content/meetups/700-june-2026.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: DevCongress June Meetup
start: "2026-06-20T10:00:00+00:00"
end: "2026-06-20T15:30:00+00:00"
description: >
A builder-focused meetup for developers, engineers, and tech enthusiasts to
learn from real-world experiences, connect with other builders, and discuss
software engineering, cloud systems, AI/ML, cybersecurity, and the work of
people actively building in the ecosystem.
cover: https://images.lumacdn.com/uploads/cr/1a1af572-a563-4695-8382-2e3063121a4e.png

location:
label: Fido, Accra
name: Fido, Opposite SSNIT Guest House, Julius Nyerere Rd, Accra
url: https://www.google.com/maps/search/?api=1&query=5.5704282%2C-0.1888184

stream_url: null
registration_url: https://luma.com/uic56yjo

speakers: []

schedule:
- time: "11:00 AM - 11:05 AM"
title: Welcome address
type: talk
lead: null
resources: []
- time: "11:10 AM - 11:55 AM"
title: Your fired employee still has access. Here is why.
type: talk
lead: Jesse
resources: []
- time: "12:00 PM - 12:30 PM"
title: "Selling SaaS to Ghanaian Businesses: What's Working So Far"
type: talk
lead: Seth
resources: []
- time: "12:35 PM - 12:45 PM"
title: Jampoll session
type: open_discussion
lead: null
resources: []
- time: "12:45 PM - 1:30 PM"
title: Design in the age of AI
type: talk
lead: Jeffrey Hinson
resources: []
- time: "1:35 PM - 2:35 PM"
title: System Design session
type: open_discussion
lead: null
resources: []
- time: "2:40 PM - 2:55 PM"
title: QuiverFood demo
type: talk
lead: Christian
resources: []
- time: "3:00 PM - 3:15 PM"
title: FlameLink demo
type: talk
lead: Codekeyz
resources: []
- time: "3:15 PM - 3:30 PM"
title: Trospot demo
type: talk
lead: Joseph
resources: []

photos: []
videos: []
23 changes: 23 additions & 0 deletions content/meetups/800-july-2026.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: DevCongress Quarterly Meetup [online]
start: "2026-07-04T18:00:00+00:00"
end: "2026-07-04T21:00:00+00:00"
description: >
A casual virtual quarterly meetup for DevCongress members to chat about what
is happening in the ecosystem, catch up, and share knowledge with each other.
No agenda, no slides, no structure. Just casual banter and real talk.
cover: /images/quarterly-meetup-template.avif

location:
label: Google Meet
name: Google Meet
url: https://luma.com/1e9h6c34

stream_url: null
registration_url: https://luma.com/1e9h6c34

speakers: []

schedule: []

photos: []
videos: []
13 changes: 8 additions & 5 deletions content/meetups/_template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,29 @@ name: "City Meetup — Month Year"
# Start and end datetimes in ISO 8601 with timezone offset.
# Format: "YYYY-MM-DDTHH:MM:SS+HH:MM"
# Examples:
# Accra (GMT+0): "2025-06-14T18:00:00+00:00"
# Lagos (GMT+1): "2025-06-14T18:00:00+01:00"
# Nairobi (GMT+3): "2025-06-14T18:00:00+03:00"
# Accra (UTC+0): "2025-06-14T18:00:00+00:00"
# Lagos (UTC+1): "2025-06-14T18:00:00+01:00"
# Nairobi (UTC+3): "2025-06-14T18:00:00+03:00"
start: "2025-01-01T18:00:00+00:00"
end: "2025-01-01T21:00:00+00:00"

# Short description shown on the listing card and detail page.
description: >
A short description of this meetup.

# Cover image URL (Google Photos, Cloudinary, etc.)
# Cover image URL or site-local image path (Google Photos, Cloudinary, /images/file.jpg, etc.)
cover: https://lh3.googleusercontent.com/your-cover-image

# ── LOCATION ─────────────────────────────────────────────────────────────────
location:
# Short venue label shown on cards and listing pages.
# Keep this compact, e.g. "Fido, Accra".
label: "Venue, City"
# Venue display name
name: "Venue Name, City"
# OPTIONAL: Google Maps or any map URL.
# Remove this line or set to null if no map link.
map_url: https://maps.google.com/?q=Venue+Name+City
url: https://maps.google.com/?q=Venue+Name+City

# ── STREAM / RECORDING ───────────────────────────────────────────────────────
# OPTIONAL. One URL serves all three states:
Expand Down
Binary file added public/images/quarterly-april-meet-up.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/quarterly-april-meetup-2.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/quarterly-meetup-template.avif
Binary file not shown.
4 changes: 2 additions & 2 deletions src/components/MeetupsSection.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
interface MeetupLocation { name: string; map_url?: string | null; }
interface MeetupLocation { label?: string; name: string; url?: string | null; }
interface Meetup {
id: string;
data: {
Expand Down Expand Up @@ -80,7 +80,7 @@ const statusConfig = {
<div class="meetup-body">
<div class="meetup-meta">
<time datetime={meetup.data.start}>{formatDate(meetup.data.start)}</time>
<span class="meetup-location">{meetup.data.location.name}</span>
<span class="meetup-location">{meetup.data.location.label ?? meetup.data.location.name}</span>
</div>
<h3 class="heading-serif meetup-name">
<a href={`/meetups/${meetup.slug}/`}>{meetup.data.name}</a>
Expand Down
11 changes: 8 additions & 3 deletions src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,23 @@ const meetups = defineCollection({
start: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/),
end: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/),
description: z.string().min(10),
cover: z.string().url(),
cover: z.string().refine(v => v.startsWith('/') || URL.canParse(v), {
message: 'cover must be a relative path or full URL',
}),
location: z.object({
label: z.string().min(2).optional(),
name: z.string().min(2),
map_url: z.string().url().nullable().optional(),
url: z.string().url().nullable().optional(),
}),
stream_url: z.string().url().nullable().optional(),
embed_stream: z.boolean().optional().default(false),
registration_url: z.string().url().nullable().optional(),
speakers: z.array(meetupSpeakerSchema).optional(),
schedule: z.array(scheduleItemSchema).optional(),
photos: z.array(z.object({
url: z.string().url(),
url: z.string().refine(v => v.startsWith('/') || URL.canParse(v), {
message: 'photo url must be a relative path or full URL',
}),
type: z.enum(['image', 'folder']).default('image'),
})).optional(),
videos: z.array(z.object({
Expand Down
19 changes: 11 additions & 8 deletions src/pages/meetups/[slug].astro
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const now = new Date();
const start = new Date(d.start);
const end = new Date(d.end);
const status = now < start ? 'upcoming' : now <= end ? 'live' : 'past';
const schedulePeriod = d.name.toLowerCase().includes('quarterly') ? 'evening' : 'day';

function formatDate(iso: string): string {
return new Date(iso).toLocaleDateString('en-GB', {
Expand All @@ -29,7 +30,7 @@ function formatDate(iso: string): string {
function formatTime(iso: string): string {
return new Date(iso).toLocaleTimeString('en-GB', {
hour: '2-digit', minute: '2-digit', timeZone: 'Africa/Accra', timeZoneName: 'short',
});
}).replace('GMT', 'UTC');
}

// ── Timezone helpers ────────────────────────────────────────
Expand All @@ -42,7 +43,8 @@ function parseTime12h(str: string): { h: number; m: number } | null {
return { h, m: +m[2] };
}
function scheduleToISO(dateISO: string, timeStr: string): string | null {
const p = parseTime12h(timeStr);
const startTime = timeStr.split(/\s[-–]\s/)[0];
const p = parseTime12h(startTime);
if (!p) return null;
const d = new Date(dateISO);
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), p.h, p.m)).toISOString();
Expand Down Expand Up @@ -90,8 +92,8 @@ const folderPhotos = photos.filter((photo) => photo.type === 'folder');
<h1 class="heading-serif meetup-title">{d.name}</h1>
<div class="meetup-hero-meta">
<span data-utc={d.start} data-display="datetime">{formatDate(d.start)} · {formatTime(d.start)}</span>
{d.location.map_url
? <a href={d.location.map_url} target="_blank" rel="noopener noreferrer" class="meetup-location-link">
{d.location.url
? <a href={d.location.url} target="_blank" rel="noopener noreferrer" class="meetup-location-link">
📍 {d.location.name}
</a>
: <span>📍 {d.location.name}</span>
Expand Down Expand Up @@ -232,15 +234,15 @@ const folderPhotos = photos.filter((photo) => photo.type === 'folder');
<div class="schedule-header-row">
<span class="eyebrow">{status === 'past' ? 'Recap' : 'Schedule'}</span>
<div class="tz-switch" role="radiogroup" aria-label="Time zone">
<span class="tz-switch-label tz-switch-label--active" data-tz-label="event">Event Time (GMT+0)</span>
<span class="tz-switch-label tz-switch-label--active" data-tz-label="event">Event Time (UTC+0)</span>
<button class="tz-switch-track" role="radio" aria-checked="true" data-tz="event">
<span class="tz-switch-knob"></span>
</button>
<span class="tz-switch-label" data-tz-label="local">My Time</span>
</div>
</div>
<h2 class="heading-serif section-heading">
{status === 'past' ? 'How the day went' : 'How the day will go'}
{status === 'past' ? `How the ${schedulePeriod} went` : `How the ${schedulePeriod} will go`}
</h2>
<ol class="schedule-list">
{scheduleItems.map((item) => (
Expand Down Expand Up @@ -705,15 +707,16 @@ const folderPhotos = photos.filter((photo) => photo.type === 'folder');
}
function formatTime(iso, tz) {
const d = new Date(iso);
return d.toLocaleTimeString('en-GB', fmtOpts(tz, { hour: '2-digit', minute: '2-digit', timeZoneName: 'short' }));
return d.toLocaleTimeString('en-GB', fmtOpts(tz, { hour: '2-digit', minute: '2-digit', timeZoneName: 'short' }))
.replace('GMT', 'UTC');
}

function getUserTZLabel() {
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
const parts = new Intl.DateTimeFormat('en', {
timeZone: tz, timeZoneName: 'shortOffset',
}).formatToParts();
return parts.find(p => p.type === 'timeZoneName')?.value || tz;
return (parts.find(p => p.type === 'timeZoneName')?.value || tz).replace('GMT', 'UTC');
}

function applyTimezone(tz) {
Expand Down
Loading
Loading