Skip to content

Commit 12762e9

Browse files
committed
Add April quarterly meetup recap
- Add the April 2026 quarterly Google Meet recap content entry. - Include the informal evening lineup: greetings, AI discussion, community feedback, funding monthly meetups, and closing banter. - Add two gallery screenshots under public/images and reference them from the meetup gallery. - Allow meetup cover and photo fields to use site-local /images paths as well as full URLs. - Use evening recap wording for quarterly meetups while keeping day wording for regular meetups. - Normalize visible timezone wording from GMT to UTC. - Update the meetup template examples to use UTC wording. - Ignore local agent-generated index/security artifacts so they stay out of project commits. - Verification: pnpm build completed successfully and generated the April quarterly meetup route.
1 parent eeb9fcb commit 12762e9

10 files changed

Lines changed: 81 additions & 17 deletions

File tree

.astro/collections/meetups.schema.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@
1919
"minLength": 10
2020
},
2121
"cover": {
22-
"type": "string",
23-
"format": "uri"
22+
"type": "string"
2423
},
2524
"location": {
2625
"type": "object",
@@ -231,8 +230,7 @@
231230
"type": "object",
232231
"properties": {
233232
"url": {
234-
"type": "string",
235-
"format": "uri"
233+
"type": "string"
236234
},
237235
"type": {
238236
"default": "image",

.astro/data-store.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

.astro/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"_variables": {
3-
"lastUpdateCheck": 1780140273670
3+
"lastUpdateCheck": 1781261337506
44
}
55
}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,8 @@ pnpm-debug.log*
2020

2121
# jetbrains setting folder
2222
.idea/
23+
24+
# Generated by local agent tooling
25+
.codebase-indexer/
26+
.sentinel/
27+
CLAUDE.md
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: DevCongress April Quarterly Meetup
2+
start: "2026-04-04T18:00:00+00:00"
3+
end: "2026-04-04T21:45:00+00:00"
4+
description: >
5+
An informal online quarterly meetup for DevCongress members in Ghana and
6+
across the diaspora. The April conversation centered on AI, coding, community
7+
feedback, and how we can keep monthly meetups funded and useful.
8+
cover: /images/quarterly-april-meet-up.jpeg
9+
10+
location:
11+
name: Google Meet
12+
map_url: null
13+
14+
stream_url: null
15+
registration_url: null
16+
17+
speakers: []
18+
19+
schedule:
20+
- time: "6:00 PM"
21+
title: Greetings, weather check & community banter
22+
type: networking
23+
lead: null
24+
resources: []
25+
- time: "6:30 PM"
26+
title: What's happening in the tech ecosystem
27+
type: open_discussion
28+
lead: null
29+
resources: []
30+
- time: "6:45 PM"
31+
title: "Open discussion: AI, coding, hallucinations & Leetcode for AI"
32+
type: open_discussion
33+
lead: null
34+
resources: []
35+
- time: "8:00 PM"
36+
title: "Community feedback: what DevCongress can improve"
37+
type: open_discussion
38+
lead: null
39+
resources: []
40+
- time: "8:45 PM"
41+
title: Funding monthly meetups and community support
42+
type: open_discussion
43+
lead: null
44+
resources: []
45+
- time: "9:30 PM"
46+
title: Open banter & close
47+
type: networking
48+
lead: null
49+
resources: []
50+
51+
photos:
52+
- url: /images/quarterly-april-meet-up.jpeg
53+
- url: /images/quarterly-april-meetup-2.jpeg
54+
55+
videos: []

content/meetups/_template.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@ name: "City Meetup — Month Year"
2727
# Start and end datetimes in ISO 8601 with timezone offset.
2828
# Format: "YYYY-MM-DDTHH:MM:SS+HH:MM"
2929
# Examples:
30-
# Accra (GMT+0): "2025-06-14T18:00:00+00:00"
31-
# Lagos (GMT+1): "2025-06-14T18:00:00+01:00"
32-
# Nairobi (GMT+3): "2025-06-14T18:00:00+03:00"
30+
# Accra (UTC+0): "2025-06-14T18:00:00+00:00"
31+
# Lagos (UTC+1): "2025-06-14T18:00:00+01:00"
32+
# Nairobi (UTC+3): "2025-06-14T18:00:00+03:00"
3333
start: "2025-01-01T18:00:00+00:00"
3434
end: "2025-01-01T21:00:00+00:00"
3535

3636
# Short description shown on the listing card and detail page.
3737
description: >
3838
A short description of this meetup.
3939
40-
# Cover image URL (Google Photos, Cloudinary, etc.)
40+
# Cover image URL or site-local image path (Google Photos, Cloudinary, /images/file.jpg, etc.)
4141
cover: https://lh3.googleusercontent.com/your-cover-image
4242

4343
# ── LOCATION ─────────────────────────────────────────────────────────────────
102 KB
Loading
102 KB
Loading

src/content.config.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ const meetups = defineCollection({
6969
start: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/),
7070
end: z.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/),
7171
description: z.string().min(10),
72-
cover: z.string().url(),
72+
cover: z.string().refine(v => v.startsWith('/') || URL.canParse(v), {
73+
message: 'cover must be a relative path or full URL',
74+
}),
7375
location: z.object({
7476
name: z.string().min(2),
7577
map_url: z.string().url().nullable().optional(),
@@ -80,7 +82,9 @@ const meetups = defineCollection({
8082
speakers: z.array(meetupSpeakerSchema).optional(),
8183
schedule: z.array(scheduleItemSchema).optional(),
8284
photos: z.array(z.object({
83-
url: z.string().url(),
85+
url: z.string().refine(v => v.startsWith('/') || URL.canParse(v), {
86+
message: 'photo url must be a relative path or full URL',
87+
}),
8488
type: z.enum(['image', 'folder']).default('image'),
8589
})).optional(),
8690
videos: z.array(z.object({

src/pages/meetups/[slug].astro

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const now = new Date();
2020
const start = new Date(d.start);
2121
const end = new Date(d.end);
2222
const status = now < start ? 'upcoming' : now <= end ? 'live' : 'past';
23+
const schedulePeriod = d.name.toLowerCase().includes('quarterly') ? 'evening' : 'day';
2324
2425
function formatDate(iso: string): string {
2526
return new Date(iso).toLocaleDateString('en-GB', {
@@ -29,7 +30,7 @@ function formatDate(iso: string): string {
2930
function formatTime(iso: string): string {
3031
return new Date(iso).toLocaleTimeString('en-GB', {
3132
hour: '2-digit', minute: '2-digit', timeZone: 'Africa/Accra', timeZoneName: 'short',
32-
});
33+
}).replace('GMT', 'UTC');
3334
}
3435
3536
// ── Timezone helpers ────────────────────────────────────────
@@ -232,15 +233,15 @@ const folderPhotos = photos.filter((photo) => photo.type === 'folder');
232233
<div class="schedule-header-row">
233234
<span class="eyebrow">{status === 'past' ? 'Recap' : 'Schedule'}</span>
234235
<div class="tz-switch" role="radiogroup" aria-label="Time zone">
235-
<span class="tz-switch-label tz-switch-label--active" data-tz-label="event">Event Time (GMT+0)</span>
236+
<span class="tz-switch-label tz-switch-label--active" data-tz-label="event">Event Time (UTC+0)</span>
236237
<button class="tz-switch-track" role="radio" aria-checked="true" data-tz="event">
237238
<span class="tz-switch-knob"></span>
238239
</button>
239240
<span class="tz-switch-label" data-tz-label="local">My Time</span>
240241
</div>
241242
</div>
242243
<h2 class="heading-serif section-heading">
243-
{status === 'past' ? 'How the day went' : 'How the day will go'}
244+
{status === 'past' ? `How the ${schedulePeriod} went` : `How the ${schedulePeriod} will go`}
244245
</h2>
245246
<ol class="schedule-list">
246247
{scheduleItems.map((item) => (
@@ -705,15 +706,16 @@ const folderPhotos = photos.filter((photo) => photo.type === 'folder');
705706
}
706707
function formatTime(iso, tz) {
707708
const d = new Date(iso);
708-
return d.toLocaleTimeString('en-GB', fmtOpts(tz, { hour: '2-digit', minute: '2-digit', timeZoneName: 'short' }));
709+
return d.toLocaleTimeString('en-GB', fmtOpts(tz, { hour: '2-digit', minute: '2-digit', timeZoneName: 'short' }))
710+
.replace('GMT', 'UTC');
709711
}
710712

711713
function getUserTZLabel() {
712714
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
713715
const parts = new Intl.DateTimeFormat('en', {
714716
timeZone: tz, timeZoneName: 'shortOffset',
715717
}).formatToParts();
716-
return parts.find(p => p.type === 'timeZoneName')?.value || tz;
718+
return (parts.find(p => p.type === 'timeZoneName')?.value || tz).replace('GMT', 'UTC');
717719
}
718720

719721
function applyTimezone(tz) {

0 commit comments

Comments
 (0)