From f5767ed32bd19416a1dc8f3b8055cbf8a9538c8b Mon Sep 17 00:00:00 2001 From: Elvis020 Date: Sat, 20 Jun 2026 05:07:39 +0000 Subject: [PATCH] Wire meetups to hosted community API - Add a remote-first meetup loader that fetches the DevCongress Community public API, normalizes the hosted JSON into the existing Astro meetup shape, resolves relative remote assets against the community app, and falls back to local YAML when the API is unavailable. - Update the home page, all-meetups index, meetup detail routes, and shared meetup section to consume the normalized loader instead of reading the meetups collection directly. - Document the hosted meetup API environment variables and add manual plus twice-weekly GitHub Pages rebuild triggers so static deploys can refresh published meetup data without a content commit. - Update the generated Astro meetup schema metadata for location.url/location.label and verify the site with pnpm build. --- .astro/collections/meetups.schema.json | 6 +- .astro/data-store.json | 2 +- .github/workflows/deploy.yml | 3 + README.md | 11 ++ src/components/MeetupsSection.astro | 20 +-- src/lib/meetups.ts | 222 +++++++++++++++++++++++++ src/pages/index.astro | 3 +- src/pages/meetups/[slug].astro | 4 +- src/pages/meetups/index.astro | 7 +- 9 files changed, 252 insertions(+), 26 deletions(-) create mode 100644 src/lib/meetups.ts diff --git a/.astro/collections/meetups.schema.json b/.astro/collections/meetups.schema.json index 0c82534..5dd8ed1 100644 --- a/.astro/collections/meetups.schema.json +++ b/.astro/collections/meetups.schema.json @@ -24,11 +24,15 @@ "location": { "type": "object", "properties": { + "label": { + "type": "string", + "minLength": 2 + }, "name": { "type": "string", "minLength": 2 }, - "map_url": { + "url": { "anyOf": [ { "type": "string", diff --git a/.astro/data-store.json b/.astro/data-store.json index 034c540..4e0408f 100644 --- a/.astro/data-store.json +++ b/.astro/data-store.json @@ -1 +1 @@ -[["Map",1,2,63,64,221,222,548,549,556,557,612,613],"activities",["Map",3,4,13,14,24,25,34,35,44,45,54,55],"chale-code",{"id":3,"data":5,"filePath":11,"digest":12},{"title":6,"description":7,"color":8,"order":9,"status":10},"Chale Code","Our mentorship program for up-and-coming developers. Structured guidance from experienced engineers in the community.","#34d399",3,"active","content/activities/chale-code.yaml","b11060e5c3673514","echo-podcast",{"id":13,"data":15,"filePath":22,"digest":23},{"title":16,"description":17,"link":18,"color":19,"order":20,"status":21},"Echo Podcast","Stories and lessons from African builders. Real experiences from engineers, founders, and product makers on the continent.","https://open.spotify.com/show/5uaPVqhudVOgS3n40Vkh4H","#22d3ee",2,"ongoing","content/activities/echo-podcast.yaml","d32f7316f24235c6","hackathon-2025",{"id":24,"data":26,"filePath":32,"digest":33},{"title":27,"description":28,"color":29,"order":30,"status":31},"DevCongress Unicef Hack25","A week of collaboration and creative problem-solving. Teams build real products in 48 hours.","#fb7185",1,"completed","content/activities/hackathon-2025.yaml","d3dbdd3cd4f034bf","how-to-build",{"id":34,"data":36,"filePath":42,"digest":43},{"title":37,"description":38,"link":39,"color":40,"order":41,"status":21},"#HowToBuild","Guides for creators and founders shipping real products. Practical knowledge on taking ideas from zero to production.","https://x.com/hashtag/HowToBuild?src=hashtag_click","#fbbf24",5,"content/activities/how-to-build.yaml","319608b851ffaceb","project-night",{"id":44,"data":46,"filePath":52,"digest":53},{"title":47,"description":48,"link":49,"color":50,"order":51,"status":21},"Project Night","A weekly virtual gathering to work on our personal projects, and share learnings. Message @aberkowitz your email for an invite.","slack://channel?team=T0A0T7A5Q&id=D3KKP1X7A","#735FFF",6,"content/activities/project-night.yaml","b5275e107bc25250","what-are-you-building",{"id":54,"data":56,"filePath":61,"digest":62},{"title":57,"description":58,"color":59,"order":60,"status":21},"What Are You Building?","A showcase of projects from our members. Share what you are working on and get feedback from the community.","#c084fc",4,"content/activities/what-are-you-building.yaml","1f1f92eff0c57ddd","admins",["Map",65,66,82,83,94,95,104,105,118,119,130,131,147,148,157,158,171,172,185,186,197,198,211,212],"andrew-smith",{"id":65,"data":67,"filePath":80,"digest":81},{"name":68,"image":69,"socials":70},"Andrew Smith","https://pbs.twimg.com/profile_images/1686721836795432960/Ghu7dKZC_400x400.jpg",[71,74,77],{"platform":72,"url":73},"x","https://twitter.com/silentworks",{"platform":75,"url":76},"website","https://donielsmith.com",{"platform":78,"url":79},"github","https://github.com/silentworks","content/admins/andrew-smith.yaml","03cea24231c1709c","anthony-acquah",{"id":82,"data":84,"filePath":92,"digest":93},{"name":85,"image":86,"socials":87},"Anthony Acquah","https://pbs.twimg.com/profile_images/3248556771/feb8ad6b5f403e70ea321d17b80df232_400x400.png",[88,90],{"platform":72,"url":89},"https://twitter.com/tony_mobster",{"platform":75,"url":91},"https://about.me/anthonyacquah","content/admins/anthony-acquah.yaml","e1fc834d78e3fe91","blossom-dugbatey",{"id":94,"data":96,"filePath":102,"digest":103},{"name":97,"image":98,"socials":99},"Blossom Dugbatey","https://ca.slack-edge.com/T0A0T7A5Q-U02LQJ83N9H-42650d9d2b97-512",[100],{"platform":72,"url":101},"https://x.com/DedeCodex","content/admins/blossom-dugbatey.yaml","b71a9d14bc7458bc","bubunyo-nyavor",{"id":104,"data":106,"filePath":116,"digest":117},{"name":107,"image":108,"socials":109},"Bubunyo Nyavor","https://pbs.twimg.com/media/FHvsk2MXMAQBovS?format=jpg&name=large",[110,112,114],{"platform":72,"url":111},"https://twitter.com/KiddBubu",{"platform":75,"url":113},"https://5error.com",{"platform":78,"url":115},"https://github.com/bubunyo","content/admins/bubunyo-nyavor.yaml","5fa8bc37ac91ad18","claude-ayitey",{"id":118,"data":120,"filePath":128,"digest":129},{"name":121,"image":122,"socials":123},"Claude Ayitey","https://api.navii.dev/avatar/milo?size=192&animated=1",[124,126],{"platform":72,"url":125},"https://twitter.com/mrayitey",{"platform":75,"url":127},"https://ayitey.com","content/admins/claude-ayitey.yaml","e7a8790dc4e29aa8","edem-kumodzi",{"id":130,"data":132,"filePath":145,"digest":146},{"name":133,"image":134,"socials":135},"Edem Kumodzi","https://avatars.githubusercontent.com/u/676790?v=4",[136,138,140,143],{"platform":72,"url":137},"https://twitter.com/edemkumodzi",{"platform":75,"url":139},"https://edemkumodzi.com",{"platform":141,"url":142},"linkedin","https://www.linkedin.com/in/edemkumodzi",{"platform":78,"url":144},"https://github.com/edem","content/admins/edem-kumodzi.yaml","a2e10a7979ea5073","elorm-adjaho",{"id":147,"data":149,"filePath":155,"digest":156},{"name":150,"image":151,"socials":152},"Elorm Adjaho","https://images.squarespace-cdn.com/content/v1/64c1ced9e4d636753f2d62a9/6c6bc5a8-d5ac-4603-8620-8d7a501cae5f/Elorm+Adjaho+_+IWL+Fellow.jpg",[153],{"platform":72,"url":154},"https://twitter.com/elormm_a","content/admins/elorm-adjaho.yaml","cd9882ad2f2adb35","francis-addai",{"id":157,"data":159,"filePath":169,"digest":170},{"name":160,"image":161,"socials":162},"Francis Addai","https://pbs.twimg.com/profile_images/1278660776232996864/YouNWmIr_400x400.jpg",[163,165,167],{"platform":72,"url":164},"https://twitter.com/faddai",{"platform":75,"url":166},"https://francisaddai.com",{"platform":78,"url":168},"https://github.com/francisaddai","content/admins/francis-addai.yaml","bfa15b76dbc718f3","nathan-sodja",{"id":171,"data":173,"filePath":183,"digest":184},{"name":174,"image":175,"socials":176},"Nathan Sodja","https://ca.slack-edge.com/T0A0T7A5Q-U01NAPY9TEC-f92ae3063f27-512",[177,179,181],{"platform":72,"url":178},"https://x.com/me_souljah",{"platform":75,"url":180},"https://hashnode.com/@nate233",{"platform":78,"url":182},"https://github.com/NateOs","content/admins/nathan-sodja.yaml","d7928e44d6152a9c","saviour-kirk",{"id":185,"data":187,"filePath":195,"digest":196},{"name":188,"image":189,"socials":190},"Saviour Kirk Agbenyegah","https://api.navii.dev/avatar/yumi?size=192&animated=1",[191,193],{"platform":72,"url":192},"https://twitter.com/savekirk",{"platform":141,"url":194},"https://www.linkedin.com/in/savekirk","content/admins/saviour-kirk.yaml","dfeb34e8b9732be4","stefan-froelich",{"id":197,"data":199,"filePath":209,"digest":210},{"name":200,"image":201,"socials":202},"Stefan Froelich","https://pbs.twimg.com/profile_images/1875679658647138304/w--j-PK8_400x400.jpg",[203,205,207],{"platform":72,"url":204},"https://x.com/TheDumbTechGuy",{"platform":75,"url":206},"https://thedumbtechguy.com",{"platform":141,"url":208},"https://www.linkedin.com/in/stefanfroelich","content/admins/stefan-froelich.yaml","517ef62ee1547fec","yaw-boakye",{"id":211,"data":213,"filePath":219,"digest":220},{"name":214,"image":215,"socials":216},"Yaw Boakye","https://pbs.twimg.com/profile_images/1680929162616291329/_jCSwpti_400x400.jpg",[217],{"platform":72,"url":218},"https://twitter.com/ejnbo","content/admins/yaw-boakye.yaml","242d14ddf4c063ea","meetups",["Map",223,224,274,275,319,320,360,361,423,424,505,506],"100-january-2026",{"id":223,"data":225,"filePath":272,"digest":273},{"name":226,"start":227,"end":228,"description":229,"cover":230,"location":231,"stream_url":234,"embed_stream":235,"registration_url":236,"speakers":237,"schedule":238,"photos":267,"videos":271},"DevCongress January Meetup","2026-01-31T10:00:00+00:00","2026-01-31T15:00:00+00:00","DevCongress is a community of tech enthusiasts passionate about growing a strong tech ecosystem in Ghana. Our monthly meetups are all about sharing knowledge, learning from each other, and building a supportive developer community. Our key focus this year is SECURITY and AI.\nEach meetup includes tech talks and hands-on workshops, and you get to choose the format that works best for you and your topic.\nFeaturing talks on Claude Code and the OWASP Top 10: 2025 release.\n","https://pbs.twimg.com/media/G-tCqAnWIAA3ny4?format=jpg&name=large",{"name":232,"map_url":233},"buro.gh, Accra","https://maps.app.goo.gl/n8u6C6TgdtW35db67",null,false,"https://luma.com/70danu1w",[],[239,244,250,254,259,263],{"time":240,"title":241,"type":242,"lead":234,"resources":243},"10:00 AM","Doors open & networking","networking",[],{"time":245,"title":246,"type":247,"lead":248,"resources":249},"10:30 AM","Talk: Claude Code When You Don't Know You're (Yet)","talk","Philip Narteh",[],{"time":251,"title":252,"type":247,"lead":234,"resources":253},"11:30 AM","Talk: OWASP Top 10: 2025 Release",[],{"time":255,"title":256,"type":257,"lead":234,"resources":258},"12:30 PM","System Design","open_discussion",[],{"time":260,"title":261,"type":247,"lead":234,"resources":262},"1:30 PM","Product & tool demos",[],{"time":264,"title":265,"type":242,"lead":234,"resources":266},"2:00 PM","Networking & close",[],[268],{"url":269,"type":270},"https://drive.google.com/drive/folders/1jEW8J7xMFu0o1YocISeLbVRy5F5i2gxd","folder",[],"content/meetups/100-january-2026.yaml","ee69fbc6ac8adc64","200-february-2026",{"id":274,"data":276,"filePath":317,"digest":318},{"name":277,"start":278,"end":279,"description":280,"cover":281,"location":282,"stream_url":234,"embed_stream":235,"registration_url":285,"speakers":286,"schedule":287,"photos":304,"videos":316},"DevCongress February Meetup","2026-02-28T10:00:00+00:00","2026-02-28T15:00:00+00:00","DevCongress is a tech community passionate about growing a strong tech ecosystem in Ghana. This meetup was co-hosted with Blossom, featuring tech talks, hands-on workshops, and showcases of applications built by members of the Ghanaian tech ecosystem. We wrapped up the event with pizza, drinks, and networking.\n","https://pbs.twimg.com/media/HAGkdjbWEAAJFba?format=jpg&name=large",{"name":283,"map_url":284},"Nyansa Square.","https://maps.app.goo.gl/NbaFFBcy89Dehq8VA","https://luma.com/frh11t0y",[],[288,290,293,297,299,301],{"time":240,"title":241,"type":242,"lead":234,"resources":289},[],{"time":245,"title":291,"type":247,"lead":234,"resources":292},"Tech talks",[],{"time":251,"title":294,"type":295,"lead":234,"resources":296},"Hands-on workshops","workshop",[],{"time":255,"title":256,"type":257,"lead":234,"resources":298},[],{"time":260,"title":261,"type":247,"lead":234,"resources":300},[],{"time":264,"title":302,"type":242,"lead":234,"resources":303},"Pizza, drinks & networking",[],[305,308,310,312,314],{"url":306,"type":307},"https://pbs.twimg.com/media/HCaAt7HWsAAWOcT?format=jpg&name=large","image",{"url":309,"type":307},"https://pbs.twimg.com/media/HCaAt7rbwAAx2qn?format=jpg&name=large",{"url":311,"type":307},"https://pbs.twimg.com/media/HCaAt7JXwAA4Hi8?format=jpg&name=large",{"url":313,"type":307},"https://pbs.twimg.com/media/HCaAt7HXUAAX75z?format=jpg&name=large",{"url":315,"type":270},"https://drive.google.com/drive/folders/1bowyXOaJiRpZ5XM6SprzJfJ85VqfUrYi",[],"content/meetups/200-february-2026.yaml","2eb27df1e9435c20","300-march-2026",{"id":319,"data":321,"filePath":358,"digest":359},{"name":322,"start":323,"end":324,"description":325,"cover":306,"location":326,"stream_url":234,"embed_stream":235,"registration_url":329,"speakers":330,"schedule":331,"photos":356,"videos":357},"DevCongress March Meetup","2026-03-28T10:00:00+00:00","2026-03-28T15:00:00+00:00","DevCongress Meetups are builder-focused gatherings for developers, engineers, and tech enthusiasts who want to learn from real-world experiences and connect with others building in the ecosystem.\nFeaturing a tech talk on memory consistency and cache coherence, workshops, a hardware session, system design discussion, product demos, a Kahoot session, and pizza & networking.\n",{"name":327,"map_url":328},"Fido, Opposite SSNIT Guest House, Julius Nyerere Rd, Accra","https://www.google.com/maps/search/?api=1&query=5.570918%2C-0.18906119999999998","https://luma.com/jf8pjncl",[],[332,334,337,340,344,346,349,352],{"time":240,"title":241,"type":242,"lead":234,"resources":333},[],{"time":245,"title":335,"type":247,"lead":234,"resources":336},"Tech Talk: Memory Consistency & Cache Coherence",[],{"time":251,"title":338,"type":295,"lead":234,"resources":339},"Workshops",[],{"time":341,"title":342,"type":295,"lead":234,"resources":343},"12:00 PM","Hardware Session",[],{"time":255,"title":256,"type":257,"lead":234,"resources":345},[],{"time":260,"title":347,"type":247,"lead":234,"resources":348},"Product Demos",[],{"time":264,"title":350,"type":257,"lead":234,"resources":351},"Kahoot Session",[],{"time":353,"title":354,"type":242,"lead":234,"resources":355},"2:30 PM","Pizza & Networking",[],[],[],"content/meetups/300-march-2026.yaml","0167a3178cc4a175","400-april-2026",{"id":360,"data":362,"filePath":421,"digest":422},{"name":363,"start":364,"end":365,"description":366,"cover":367,"location":368,"stream_url":234,"embed_stream":235,"registration_url":369,"speakers":370,"schedule":371,"photos":391,"videos":420},"DevCongress April Meetup","2026-04-25T10:00:00+00:00","2026-04-25T15:00:00+00:00","DevCongress Meetups are builder-focused gatherings for developers, engineers, and tech enthusiasts who want to learn from real-world experiences and connect with others building in the ecosystem.\nFeaturing a tech talk on memory consistency and cache coherence, a design session with UXDerrick, system design discussion, product/tool demos, a Kahoot session, and pizza & networking.\n","https://pbs.twimg.com/media/HCLwbPBXYAAzGdM?format=jpg&name=large",{"name":327,"map_url":328},"https://lu.ma/qqx9uk6u",[],[372,374,377,381,383,387,389],{"time":240,"title":241,"type":242,"lead":234,"resources":373},[],{"time":245,"title":375,"type":247,"lead":234,"resources":376},"Tech Talk: Memory Consistency & Cache Coherence (cont'd)",[],{"time":251,"title":378,"type":247,"lead":379,"resources":380},"Design Session with UXDerrick","UXDerrick",[],{"time":341,"title":256,"type":257,"lead":234,"resources":382},[],{"time":384,"title":385,"type":247,"lead":234,"resources":386},"12:45 PM","Product & Tool Demos",[],{"time":260,"title":350,"type":257,"lead":234,"resources":388},[],{"time":264,"title":354,"type":242,"lead":234,"resources":390},[],[392,394,396,398,400,402,404,406,408,410,412,414,416,418],{"url":393,"type":307},"https://pbs.twimg.com/media/HGwrt-RagAAgX_-?format=jpg&name=large",{"url":395,"type":307},"https://pbs.twimg.com/media/HGwm9MKbQAAwyPr?format=jpg&name=large",{"url":397,"type":307},"https://pbs.twimg.com/media/HGwYEdhakAAvmCm?format=jpg&name=large",{"url":399,"type":307},"https://pbs.twimg.com/media/HGwMz61a0AAor9d?format=jpg&name=large",{"url":401,"type":307},"https://pbs.twimg.com/media/HGwC7dEbMAAU9os?format=jpg&name=large",{"url":403,"type":307},"https://pbs.twimg.com/media/HGv0RwAaAAAfNVH?format=jpg&name=large",{"url":405,"type":307},"https://pbs.twimg.com/media/HEgj8EFWgAAtqPn?format=jpg&name=large",{"url":407,"type":307},"https://pbs.twimg.com/media/HEgjh9YXcAAubIw?format=jpg&name=large",{"url":409,"type":307},"https://pbs.twimg.com/media/HEge8jJa0AA6Lg9?format=jpg&name=large",{"url":411,"type":307},"https://pbs.twimg.com/media/HEgT9zma8AAX3tn?format=jpg&name=large",{"url":413,"type":307},"https://pbs.twimg.com/media/HEgSynGbIAAuJLn?format=jpg&name=large",{"url":415,"type":307},"https://pbs.twimg.com/media/HEgAxq1WQAAb8bc?format=jpg&name=large",{"url":417,"type":307},"https://pbs.twimg.com/media/HEf5kkRbEAAyxIl?format=jpg&name=large",{"url":419,"type":307},"https://pbs.twimg.com/media/HEfovhhakAAE4Fl?format=jpg&name=large",[],"content/meetups/400-april-2026.yaml","870a5d69b02dc314","500-may-2026",{"id":423,"data":425,"filePath":503,"digest":504},{"name":426,"start":427,"end":428,"description":429,"cover":430,"location":431,"stream_url":432,"embed_stream":235,"registration_url":433,"speakers":434,"schedule":435,"photos":499,"videos":502},"DevCongress May Meetup","2026-05-30T10:00:00+00:00","2026-05-30T16:00:00+00:00","Featuring product demos from Sankofa, OpenMirror, and JestryAI, a microcontroller workshop with Manny, a session from Old Mutual, system design discussion, FIDCON partnership announcement, and a panel discussion.\n","https://pbs.twimg.com/media/HG_xUr-WIAABl-e?format=jpg&name=medium",{"name":232,"map_url":233},"https://x.com/i/broadcasts/1aKbddMWOjaJX","https://lu.ma/3b0il40a",[],[436,438,442,449,453,456,463,467,473,481,488],{"time":240,"title":241,"type":242,"lead":234,"resources":437},[],{"time":439,"title":440,"type":247,"lead":234,"resources":441},"11:00 AM","Welcome address",[],{"time":443,"title":444,"type":247,"lead":234,"resources":445},"11:10 AM","Product Demo: Sankofa",[446],{"title":447,"url":448},"Website","https://sankofa.dev",{"time":251,"title":450,"type":295,"lead":451,"resources":452},"Manny — Demo with Microcontrollers","@willofdaedalus",[],{"time":255,"title":454,"type":247,"lead":234,"resources":455},"Product Demo: OpenMirror",[],{"time":457,"title":458,"type":247,"lead":234,"resources":459},"12:50 PM","Old Mutual",[460],{"title":461,"url":462},"Startup Resources","https://ecosystem.oldmutual.com.gh",{"time":464,"title":465,"type":247,"lead":234,"resources":466},"1:25 PM","FIDCON partnership announcement & promo code",[],{"time":468,"title":469,"type":247,"lead":234,"resources":470},"1:35 PM","Product Demo: JestryAI",[471],{"title":447,"url":472},"https://www.jestryai.com/",{"time":474,"title":475,"type":257,"lead":476,"resources":477},"1:50 PM","System Design Session (Blacko's Detty December Concert)","Tonny-Bright",[478],{"title":479,"url":480},"Problem Discription","https://docs.google.com/document/d/1qGhdc7L_PoCPuEGaheQ5RT5C-ML9-BzzfVQjmKTfSTQ/edit?usp=sharing",{"time":482,"title":483,"type":247,"lead":234,"resources":484},"2:50 PM","DevCongress Support Announcement",[485],{"title":486,"url":487},"Feedback Form","https://docs.google.com/forms/d/e/1FAIpQLSco-46QfOtkU70eOoFmskRa5TPLBGoVI34KudArxmrzO3dFdw/viewform",{"time":489,"title":490,"type":491,"lead":234,"resources":492},"3:10 PM","Panel Discussion - The New NITA Bill","panel",[493,496],{"title":494,"url":495},"NATIONAL INFORMATION TECHNOLOGY AUTHORITY BILL, 2025","https://nita.gov.gh/wp-content/uploads/2025/NITA-2008-act-2025-1.pdf",{"title":497,"url":498},"NITA REGULATORY | IT SERVICE PROVIDER REGISTRATION (FEES)","https://regulatory.nita.gov.gh/portal/regulatory/registration-of-it-firms",[500],{"url":501,"type":270},"https://drive.google.com/drive/folders/1NblGjqakAaTWZ7SBvTZBsImvnpQzhXrQ?usp=sharing",[],"content/meetups/500-may-2026.yaml","0a064f6e865ce3f3","600-april-2026",{"id":505,"data":507,"filePath":546,"digest":547},{"name":508,"start":509,"end":510,"description":511,"cover":512,"location":513,"stream_url":234,"embed_stream":235,"registration_url":234,"speakers":515,"schedule":516,"photos":541,"videos":545},"DevCongress April Quarterly Meetup","2026-04-04T18:00:00+00:00","2026-04-04T21:45:00+00:00","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.\n","/images/quarterly-april-meet-up.jpeg",{"name":514,"map_url":234},"Google Meet",[],[517,521,525,529,533,537],{"time":518,"title":519,"type":242,"lead":234,"resources":520},"6:00 PM","Greetings, weather check & community banter",[],{"time":522,"title":523,"type":257,"lead":234,"resources":524},"6:30 PM","What's happening in the tech ecosystem",[],{"time":526,"title":527,"type":257,"lead":234,"resources":528},"6:45 PM","Open discussion: AI, coding, hallucinations & Leetcode for AI",[],{"time":530,"title":531,"type":257,"lead":234,"resources":532},"8:00 PM","Community feedback: what DevCongress can improve",[],{"time":534,"title":535,"type":257,"lead":234,"resources":536},"8:45 PM","Funding monthly meetups and community support",[],{"time":538,"title":539,"type":242,"lead":234,"resources":540},"9:30 PM","Open banter & close",[],[542,543],{"url":512,"type":307},{"url":544,"type":307},"/images/quarterly-april-meetup-2.jpeg",[],"content/meetups/600-april-2026.yaml","7ff3a15e046317d1","meta::meta",["Map",550,551,552,553,554,555],"astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://devcongress.org\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":\"127.0.0.1\",\"port\":4321,\"allowedHosts\":[\"127.0.0.1\",\"127.0.0.1\",\"127.0.0.1\",\"127.0.0.1\"]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"dangerouslyProcessSVG\":false,\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"processor\":{\"name\":\"unified\",\"options\":{\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{}}}},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"csp\":false,\"actionBodySizeLimit\":1048576,\"serverIslandBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"prerenderConflictBehavior\":\"warn\",\"experimental\":{\"advancedRouting\":false,\"clientPrerender\":false,\"contentIntellisense\":false,\"chromeDevtoolsWorkspace\":false,\"rustCompiler\":false,\"queuedRendering\":{\"enabled\":false}},\"legacy\":{\"collectionsBackwardsCompat\":false}}","astro-version","6.4.2","content-config-digest","38bcadffecda837c","partners",["Map",558,559,567,568,576,577,585,586,594,595,603,604],"fido",{"id":558,"data":560,"filePath":565,"digest":566},{"name":561,"logo":562,"url":563,"backgroundColor":564},"Fido Credit","https://cdn.prod.website-files.com/62fa16d8d83816b0fc5defc4/62fcf5fdb02a38631df13edb_footer_logo.svg","https://gh.fido.money/","#d6086b","content/partners/fido.yaml","5b128ec983e6ea16","kweku_tech",{"id":567,"data":569,"filePath":574,"digest":575},{"name":570,"logo":571,"url":572,"backgroundColor":573},"Kweku Tech Media","https://www.kweku.tech/_next/image?url=%2Flogo.svg&w=256&q=75","https://kweku.tech","#070d0d","content/partners/kweku_tech.yaml","e8c7363657c5fc75","mest",{"id":576,"data":578,"filePath":583,"digest":584},{"name":579,"logo":580,"url":581,"backgroundColor":582},"MEST Africa","https://meltwater.org/wp-content/uploads/2024/01/MEST-Logo_landscape_03-e1704132329210-300x49.png","https://meltwater.org/","#ffffff","content/partners/mest.yaml","3ab53258ae155d60","old-mutual",{"id":585,"data":587,"filePath":592,"digest":593},{"name":588,"logo":589,"url":590,"backgroundColor":591},"Old Mutual Insurance Ltd.","https://xplorio.com/xplorio/categories/28627/OM_Logo_Horizonal_Reverse_on_Vignette_RGB_1583910489.jpg?width=960&height=440","https://www.oldmutual.com/","#60b84888","content/partners/old-mutual.yaml","340abccde4b94deb","paystack",{"id":594,"data":596,"filePath":601,"digest":602},{"name":597,"logo":598,"url":599,"backgroundColor":600},"Paystack","https://cdn-assets-cloud.frontify.com/s3/frontify-cloud-files-us/eyJwYXRoIjoiZnJvbnRpZnlcL2FjY291bnRzXC8yYVwvMTQxNzczXC9wcm9qZWN0c1wvMTc4NjE0XC9hc3NldHNcL2E5XC8zNTI0MTM2XC9jYmJkNzA1OTEyN2NlOGU3ZDI5ZDM5NmJkMzhmMzkwNi0xNTY5MjE3MjIzLnBuZyJ9:frontify:xd65O1_BCk88mGEkjq6maiXPQH5Ol5Vhn97tbY1vl0c?width=1600&format=webp&quality=100","https://paystack.com/","#f1fef4f2","content/partners/paystack.yaml","c589d5711904ae48","unicef",{"id":603,"data":605,"filePath":610,"digest":611},{"name":606,"logo":607,"url":608,"backgroundColor":609},"UNICEF Ghana","https://www.unicef.org/ghana/sites/unicef.org.ghana/files/styles/logo/public/English_23.png.webp?itok=TAurVHy1","https://www.unicef.org/ghana/","#008bbf","content/partners/unicef.yaml","eaf47158df9eab86","site",["Map",612,614],{"id":612,"data":615,"filePath":703},{"community_slack_url":616,"paystack_url":617,"youtube_embed_url":618,"stats":619,"socials":628,"roles":644,"mission_section":660,"donate_section":681,"hero":692},"https://devcongress-community.slack.com/join/shared_invite/zt-3elxv0f0y-U6JoK8Al4ExQp8ERaS8uwg#/shared-invite/email","https://paystack.shop/pay/devcongress-meetup","https://www.youtube-nocookie.com/embed/vNWWGWXjybg?rel=0&modestbranding=1&playsinline=1",[620,624],{"value":621,"label":622,"description":623},"6000+","Community Members","Developers, Designers, Founders, Hackers and Makers across Africa.",{"value":625,"label":626,"description":627},"10+","Years","Building Community since 2015.",[629,631,633,635,638,641],{"platform":141,"url":630},"https://www.linkedin.com/company/devcongress",{"platform":72,"url":632},"https://x.com/devcongress",{"platform":78,"url":634},"https://github.com/devcongress",{"platform":636,"url":637},"youtube","https://youtube.com/devcongress",{"platform":639,"url":640},"facebook","https://facebook.com/devcongress",{"platform":642,"url":643},"instagram","https://www.instagram.com/devcongress/",[645,646,647,648,649,650,651,652,653,654,655,656,657,658,659],"Software Engineer","Product Manager","UX Designer","Data Scientist","DevOps Engineer","Mobile Developer","Frontend Developer","Backend Engineer","QA Engineer","Tech Lead","Founder","Marketer","Legal Advisor","Sales Professional","System Architect",{"eyebrow":661,"headline":662,"body":663,"cta":664,"items":665},"Mission","We exist because Africa's builders deserve a community that takes them *seriously*.","DevCongress is a movement. No paywalls, no gatekeeping — just builders helping builders grow across Africa and beyond.","Join the community",[666,669,672,675,678],{"heading":667,"description":668},"Build capacity & confidence through community led *mentorship*.","Over 500+ professionals upskilled through mentorship, events, and hands-on feedback.",{"heading":670,"description":671},"Expose tech talents to a rich array of *resources*.","30+ community sessions, 100+ shared resources, and growing.",{"heading":673,"description":674},"Contribute to innovative and community-driven projects that create *real impact*.","Contributed to 10+ community-led tools powering innovation across Africa.",{"heading":676,"description":677},"Harness the collective strength of community to solve *real problems*.","From solo learners to startup founders, we connect and grow together.",{"heading":679,"description":680},"Build the *changemakers* who shape a nation's technology policy & future.","From community organising to national policy discussions, DevCongress members are agents of change — shaping the future of technology and society across Africa.",{"eyebrow":682,"heading":683,"body":684,"cta_text":685,"uses_header":686,"uses_items":687},"Support","Keep DevCongress running.","DevCongress is community-funded without the strings of corporate backing. Your contribution keeps the lights on and the community growing.","Donate via Paystack","Your support funds",[688,689,690,691],"Monthly meetups across Ghana","Dedicated Support Programs","Community Tools and Shared Resources","Critical Infrastructure to Run the Community",{"eyebrow":693,"headline":694,"cta_primary":695,"cta_secondary":698,"video_label":701,"video_live":702},"Africa's tech community","Where Africa's builders connect, collaborate and grow.",{"text":696,"color":697},"Join us on Slack","pink",{"text":699,"color":700},"Support the Mission","outline","dev:congress{}; — community highlight","● Live Community","content/site.yaml"] \ No newline at end of file +[["Map",1,2,63,64,221,222,624,625,632,633,688,689],"activities",["Map",3,4,13,14,24,25,34,35,44,45,54,55],"chale-code",{"id":3,"data":5,"filePath":11,"digest":12},{"title":6,"description":7,"color":8,"order":9,"status":10},"Chale Code","Our mentorship program for up-and-coming developers. Structured guidance from experienced engineers in the community.","#34d399",3,"active","content/activities/chale-code.yaml","b11060e5c3673514","echo-podcast",{"id":13,"data":15,"filePath":22,"digest":23},{"title":16,"description":17,"link":18,"color":19,"order":20,"status":21},"Echo Podcast","Stories and lessons from African builders. Real experiences from engineers, founders, and product makers on the continent.","https://open.spotify.com/show/5uaPVqhudVOgS3n40Vkh4H","#22d3ee",2,"ongoing","content/activities/echo-podcast.yaml","d32f7316f24235c6","hackathon-2025",{"id":24,"data":26,"filePath":32,"digest":33},{"title":27,"description":28,"color":29,"order":30,"status":31},"DevCongress Unicef Hack25","A week of collaboration and creative problem-solving. Teams build real products in 48 hours.","#fb7185",1,"completed","content/activities/hackathon-2025.yaml","d3dbdd3cd4f034bf","how-to-build",{"id":34,"data":36,"filePath":42,"digest":43},{"title":37,"description":38,"link":39,"color":40,"order":41,"status":21},"#HowToBuild","Guides for creators and founders shipping real products. Practical knowledge on taking ideas from zero to production.","https://x.com/hashtag/HowToBuild?src=hashtag_click","#fbbf24",5,"content/activities/how-to-build.yaml","319608b851ffaceb","project-night",{"id":44,"data":46,"filePath":52,"digest":53},{"title":47,"description":48,"link":49,"color":50,"order":51,"status":21},"Project Night","A weekly virtual gathering to work on our personal projects, and share learnings. Message @aberkowitz your email for an invite.","slack://channel?team=T0A0T7A5Q&id=D3KKP1X7A","#735FFF",6,"content/activities/project-night.yaml","b5275e107bc25250","what-are-you-building",{"id":54,"data":56,"filePath":61,"digest":62},{"title":57,"description":58,"color":59,"order":60,"status":21},"What Are You Building?","A showcase of projects from our members. Share what you are working on and get feedback from the community.","#c084fc",4,"content/activities/what-are-you-building.yaml","1f1f92eff0c57ddd","admins",["Map",65,66,82,83,94,95,104,105,118,119,130,131,147,148,157,158,171,172,185,186,197,198,211,212],"andrew-smith",{"id":65,"data":67,"filePath":80,"digest":81},{"name":68,"image":69,"socials":70},"Andrew Smith","https://pbs.twimg.com/profile_images/1686721836795432960/Ghu7dKZC_400x400.jpg",[71,74,77],{"platform":72,"url":73},"x","https://twitter.com/silentworks",{"platform":75,"url":76},"website","https://donielsmith.com",{"platform":78,"url":79},"github","https://github.com/silentworks","content/admins/andrew-smith.yaml","03cea24231c1709c","anthony-acquah",{"id":82,"data":84,"filePath":92,"digest":93},{"name":85,"image":86,"socials":87},"Anthony Acquah","https://pbs.twimg.com/profile_images/3248556771/feb8ad6b5f403e70ea321d17b80df232_400x400.png",[88,90],{"platform":72,"url":89},"https://twitter.com/tony_mobster",{"platform":75,"url":91},"https://about.me/anthonyacquah","content/admins/anthony-acquah.yaml","e1fc834d78e3fe91","blossom-dugbatey",{"id":94,"data":96,"filePath":102,"digest":103},{"name":97,"image":98,"socials":99},"Blossom Dugbatey","https://ca.slack-edge.com/T0A0T7A5Q-U02LQJ83N9H-42650d9d2b97-512",[100],{"platform":72,"url":101},"https://x.com/DedeCodex","content/admins/blossom-dugbatey.yaml","b71a9d14bc7458bc","bubunyo-nyavor",{"id":104,"data":106,"filePath":116,"digest":117},{"name":107,"image":108,"socials":109},"Bubunyo Nyavor","https://pbs.twimg.com/media/FHvsk2MXMAQBovS?format=jpg&name=large",[110,112,114],{"platform":72,"url":111},"https://twitter.com/KiddBubu",{"platform":75,"url":113},"https://5error.com",{"platform":78,"url":115},"https://github.com/bubunyo","content/admins/bubunyo-nyavor.yaml","5fa8bc37ac91ad18","claude-ayitey",{"id":118,"data":120,"filePath":128,"digest":129},{"name":121,"image":122,"socials":123},"Claude Ayitey","https://api.navii.dev/avatar/milo?size=192&animated=1",[124,126],{"platform":72,"url":125},"https://twitter.com/mrayitey",{"platform":75,"url":127},"https://ayitey.com","content/admins/claude-ayitey.yaml","e7a8790dc4e29aa8","edem-kumodzi",{"id":130,"data":132,"filePath":145,"digest":146},{"name":133,"image":134,"socials":135},"Edem Kumodzi","https://avatars.githubusercontent.com/u/676790?v=4",[136,138,140,143],{"platform":72,"url":137},"https://twitter.com/edemkumodzi",{"platform":75,"url":139},"https://edemkumodzi.com",{"platform":141,"url":142},"linkedin","https://www.linkedin.com/in/edemkumodzi",{"platform":78,"url":144},"https://github.com/edem","content/admins/edem-kumodzi.yaml","a2e10a7979ea5073","elorm-adjaho",{"id":147,"data":149,"filePath":155,"digest":156},{"name":150,"image":151,"socials":152},"Elorm Adjaho","https://images.squarespace-cdn.com/content/v1/64c1ced9e4d636753f2d62a9/6c6bc5a8-d5ac-4603-8620-8d7a501cae5f/Elorm+Adjaho+_+IWL+Fellow.jpg",[153],{"platform":72,"url":154},"https://twitter.com/elormm_a","content/admins/elorm-adjaho.yaml","cd9882ad2f2adb35","francis-addai",{"id":157,"data":159,"filePath":169,"digest":170},{"name":160,"image":161,"socials":162},"Francis Addai","https://pbs.twimg.com/profile_images/1278660776232996864/YouNWmIr_400x400.jpg",[163,165,167],{"platform":72,"url":164},"https://twitter.com/faddai",{"platform":75,"url":166},"https://francisaddai.com",{"platform":78,"url":168},"https://github.com/francisaddai","content/admins/francis-addai.yaml","bfa15b76dbc718f3","nathan-sodja",{"id":171,"data":173,"filePath":183,"digest":184},{"name":174,"image":175,"socials":176},"Nathan Sodja","https://ca.slack-edge.com/T0A0T7A5Q-U01NAPY9TEC-f92ae3063f27-512",[177,179,181],{"platform":72,"url":178},"https://x.com/me_souljah",{"platform":75,"url":180},"https://hashnode.com/@nate233",{"platform":78,"url":182},"https://github.com/NateOs","content/admins/nathan-sodja.yaml","d7928e44d6152a9c","saviour-kirk",{"id":185,"data":187,"filePath":195,"digest":196},{"name":188,"image":189,"socials":190},"Saviour Kirk Agbenyegah","https://api.navii.dev/avatar/yumi?size=192&animated=1",[191,193],{"platform":72,"url":192},"https://twitter.com/savekirk",{"platform":141,"url":194},"https://www.linkedin.com/in/savekirk","content/admins/saviour-kirk.yaml","dfeb34e8b9732be4","stefan-froelich",{"id":197,"data":199,"filePath":209,"digest":210},{"name":200,"image":201,"socials":202},"Stefan Froelich","https://pbs.twimg.com/profile_images/1875679658647138304/w--j-PK8_400x400.jpg",[203,205,207],{"platform":72,"url":204},"https://x.com/TheDumbTechGuy",{"platform":75,"url":206},"https://thedumbtechguy.com",{"platform":141,"url":208},"https://www.linkedin.com/in/stefanfroelich","content/admins/stefan-froelich.yaml","517ef62ee1547fec","yaw-boakye",{"id":211,"data":213,"filePath":219,"digest":220},{"name":214,"image":215,"socials":216},"Yaw Boakye","https://pbs.twimg.com/profile_images/1680929162616291329/_jCSwpti_400x400.jpg",[217],{"platform":72,"url":218},"https://twitter.com/ejnbo","content/admins/yaw-boakye.yaml","242d14ddf4c063ea","meetups",["Map",223,224,274,275,320,321,362,363,425,426,507,508,550,551,608,609],"100-january-2026",{"id":223,"data":225,"filePath":272,"digest":273},{"name":226,"start":227,"end":228,"description":229,"cover":230,"location":231,"stream_url":234,"embed_stream":235,"registration_url":236,"speakers":237,"schedule":238,"photos":267,"videos":271},"DevCongress January Meetup","2026-01-31T10:00:00+00:00","2026-01-31T15:00:00+00:00","DevCongress is a community of tech enthusiasts passionate about growing a strong tech ecosystem in Ghana. Our monthly meetups are all about sharing knowledge, learning from each other, and building a supportive developer community. Our key focus this year is SECURITY and AI.\nEach meetup includes tech talks and hands-on workshops, and you get to choose the format that works best for you and your topic.\nFeaturing talks on Claude Code and the OWASP Top 10: 2025 release.\n","https://pbs.twimg.com/media/G-tCqAnWIAA3ny4?format=jpg&name=large",{"label":232,"name":232,"url":233},"buro.gh, Accra","https://maps.app.goo.gl/n8u6C6TgdtW35db67",null,false,"https://luma.com/70danu1w",[],[239,244,250,254,259,263],{"time":240,"title":241,"type":242,"lead":234,"resources":243},"10:00 AM","Doors open & networking","networking",[],{"time":245,"title":246,"type":247,"lead":248,"resources":249},"10:30 AM","Talk: Claude Code When You Don't Know You're (Yet)","talk","Philip Narteh",[],{"time":251,"title":252,"type":247,"lead":234,"resources":253},"11:30 AM","Talk: OWASP Top 10: 2025 Release",[],{"time":255,"title":256,"type":257,"lead":234,"resources":258},"12:30 PM","System Design","open_discussion",[],{"time":260,"title":261,"type":247,"lead":234,"resources":262},"1:30 PM","Product & tool demos",[],{"time":264,"title":265,"type":242,"lead":234,"resources":266},"2:00 PM","Networking & close",[],[268],{"url":269,"type":270},"https://drive.google.com/drive/folders/1jEW8J7xMFu0o1YocISeLbVRy5F5i2gxd","folder",[],"content/meetups/100-january-2026.yaml","9e24ea49d84aecc6","200-february-2026",{"id":274,"data":276,"filePath":318,"digest":319},{"name":277,"start":278,"end":279,"description":280,"cover":281,"location":282,"stream_url":234,"embed_stream":235,"registration_url":286,"speakers":287,"schedule":288,"photos":305,"videos":317},"DevCongress February Meetup","2026-02-28T10:00:00+00:00","2026-02-28T15:00:00+00:00","DevCongress is a tech community passionate about growing a strong tech ecosystem in Ghana. This meetup was co-hosted with Blossom, featuring tech talks, hands-on workshops, and showcases of applications built by members of the Ghanaian tech ecosystem. We wrapped up the event with pizza, drinks, and networking.\n","https://pbs.twimg.com/media/HAGkdjbWEAAJFba?format=jpg&name=large",{"label":283,"name":284,"url":285},"Nyansa Square, Accra","Nyansa Square.","https://maps.app.goo.gl/NbaFFBcy89Dehq8VA","https://luma.com/frh11t0y",[],[289,291,294,298,300,302],{"time":240,"title":241,"type":242,"lead":234,"resources":290},[],{"time":245,"title":292,"type":247,"lead":234,"resources":293},"Tech talks",[],{"time":251,"title":295,"type":296,"lead":234,"resources":297},"Hands-on workshops","workshop",[],{"time":255,"title":256,"type":257,"lead":234,"resources":299},[],{"time":260,"title":261,"type":247,"lead":234,"resources":301},[],{"time":264,"title":303,"type":242,"lead":234,"resources":304},"Pizza, drinks & networking",[],[306,309,311,313,315],{"url":307,"type":308},"https://pbs.twimg.com/media/HCaAt7HWsAAWOcT?format=jpg&name=large","image",{"url":310,"type":308},"https://pbs.twimg.com/media/HCaAt7rbwAAx2qn?format=jpg&name=large",{"url":312,"type":308},"https://pbs.twimg.com/media/HCaAt7JXwAA4Hi8?format=jpg&name=large",{"url":314,"type":308},"https://pbs.twimg.com/media/HCaAt7HXUAAX75z?format=jpg&name=large",{"url":316,"type":270},"https://drive.google.com/drive/folders/1bowyXOaJiRpZ5XM6SprzJfJ85VqfUrYi",[],"content/meetups/200-february-2026.yaml","2e65e95e9bbf12b7","300-march-2026",{"id":320,"data":322,"filePath":360,"digest":361},{"name":323,"start":324,"end":325,"description":326,"cover":307,"location":327,"stream_url":234,"embed_stream":235,"registration_url":331,"speakers":332,"schedule":333,"photos":358,"videos":359},"DevCongress March Meetup","2026-03-28T10:00:00+00:00","2026-03-28T15:00:00+00:00","DevCongress Meetups are builder-focused gatherings for developers, engineers, and tech enthusiasts who want to learn from real-world experiences and connect with others building in the ecosystem.\nFeaturing a tech talk on memory consistency and cache coherence, workshops, a hardware session, system design discussion, product demos, a Kahoot session, and pizza & networking.\n",{"label":328,"name":329,"url":330},"Fido, Accra","Fido, Opposite SSNIT Guest House, Julius Nyerere Rd, Accra","https://www.google.com/maps/search/?api=1&query=5.570918%2C-0.18906119999999998","https://luma.com/jf8pjncl",[],[334,336,339,342,346,348,351,354],{"time":240,"title":241,"type":242,"lead":234,"resources":335},[],{"time":245,"title":337,"type":247,"lead":234,"resources":338},"Tech Talk: Memory Consistency & Cache Coherence",[],{"time":251,"title":340,"type":296,"lead":234,"resources":341},"Workshops",[],{"time":343,"title":344,"type":296,"lead":234,"resources":345},"12:00 PM","Hardware Session",[],{"time":255,"title":256,"type":257,"lead":234,"resources":347},[],{"time":260,"title":349,"type":247,"lead":234,"resources":350},"Product Demos",[],{"time":264,"title":352,"type":257,"lead":234,"resources":353},"Kahoot Session",[],{"time":355,"title":356,"type":242,"lead":234,"resources":357},"2:30 PM","Pizza & Networking",[],[],[],"content/meetups/300-march-2026.yaml","eee697a8f15ce33f","400-april-2026",{"id":362,"data":364,"filePath":423,"digest":424},{"name":365,"start":366,"end":367,"description":368,"cover":369,"location":370,"stream_url":234,"embed_stream":235,"registration_url":371,"speakers":372,"schedule":373,"photos":393,"videos":422},"DevCongress April Meetup","2026-04-25T10:00:00+00:00","2026-04-25T15:00:00+00:00","DevCongress Meetups are builder-focused gatherings for developers, engineers, and tech enthusiasts who want to learn from real-world experiences and connect with others building in the ecosystem.\nFeaturing a tech talk on memory consistency and cache coherence, a design session with UXDerrick, system design discussion, product/tool demos, a Kahoot session, and pizza & networking.\n","https://pbs.twimg.com/media/HCLwbPBXYAAzGdM?format=jpg&name=large",{"label":328,"name":329,"url":330},"https://lu.ma/qqx9uk6u",[],[374,376,379,383,385,389,391],{"time":240,"title":241,"type":242,"lead":234,"resources":375},[],{"time":245,"title":377,"type":247,"lead":234,"resources":378},"Tech Talk: Memory Consistency & Cache Coherence (cont'd)",[],{"time":251,"title":380,"type":247,"lead":381,"resources":382},"Design Session with UXDerrick","UXDerrick",[],{"time":343,"title":256,"type":257,"lead":234,"resources":384},[],{"time":386,"title":387,"type":247,"lead":234,"resources":388},"12:45 PM","Product & Tool Demos",[],{"time":260,"title":352,"type":257,"lead":234,"resources":390},[],{"time":264,"title":356,"type":242,"lead":234,"resources":392},[],[394,396,398,400,402,404,406,408,410,412,414,416,418,420],{"url":395,"type":308},"https://pbs.twimg.com/media/HGwrt-RagAAgX_-?format=jpg&name=large",{"url":397,"type":308},"https://pbs.twimg.com/media/HGwm9MKbQAAwyPr?format=jpg&name=large",{"url":399,"type":308},"https://pbs.twimg.com/media/HGwYEdhakAAvmCm?format=jpg&name=large",{"url":401,"type":308},"https://pbs.twimg.com/media/HGwMz61a0AAor9d?format=jpg&name=large",{"url":403,"type":308},"https://pbs.twimg.com/media/HGwC7dEbMAAU9os?format=jpg&name=large",{"url":405,"type":308},"https://pbs.twimg.com/media/HGv0RwAaAAAfNVH?format=jpg&name=large",{"url":407,"type":308},"https://pbs.twimg.com/media/HEgj8EFWgAAtqPn?format=jpg&name=large",{"url":409,"type":308},"https://pbs.twimg.com/media/HEgjh9YXcAAubIw?format=jpg&name=large",{"url":411,"type":308},"https://pbs.twimg.com/media/HEge8jJa0AA6Lg9?format=jpg&name=large",{"url":413,"type":308},"https://pbs.twimg.com/media/HEgT9zma8AAX3tn?format=jpg&name=large",{"url":415,"type":308},"https://pbs.twimg.com/media/HEgSynGbIAAuJLn?format=jpg&name=large",{"url":417,"type":308},"https://pbs.twimg.com/media/HEgAxq1WQAAb8bc?format=jpg&name=large",{"url":419,"type":308},"https://pbs.twimg.com/media/HEf5kkRbEAAyxIl?format=jpg&name=large",{"url":421,"type":308},"https://pbs.twimg.com/media/HEfovhhakAAE4Fl?format=jpg&name=large",[],"content/meetups/400-april-2026.yaml","7ea9405e3d20fe4e","500-may-2026",{"id":425,"data":427,"filePath":505,"digest":506},{"name":428,"start":429,"end":430,"description":431,"cover":432,"location":433,"stream_url":434,"embed_stream":235,"registration_url":435,"speakers":436,"schedule":437,"photos":501,"videos":504},"DevCongress May Meetup","2026-05-30T10:00:00+00:00","2026-05-30T16:00:00+00:00","Featuring product demos from Sankofa, OpenMirror, and JestryAI, a microcontroller workshop with Manny, a session from Old Mutual, system design discussion, FIDCON partnership announcement, and a panel discussion.\n","https://pbs.twimg.com/media/HG_xUr-WIAABl-e?format=jpg&name=medium",{"label":232,"name":232,"url":233},"https://x.com/i/broadcasts/1aKbddMWOjaJX","https://lu.ma/3b0il40a",[],[438,440,444,451,455,458,465,469,475,483,490],{"time":240,"title":241,"type":242,"lead":234,"resources":439},[],{"time":441,"title":442,"type":247,"lead":234,"resources":443},"11:00 AM","Welcome address",[],{"time":445,"title":446,"type":247,"lead":234,"resources":447},"11:10 AM","Product Demo: Sankofa",[448],{"title":449,"url":450},"Website","https://sankofa.dev",{"time":251,"title":452,"type":296,"lead":453,"resources":454},"Manny — Demo with Microcontrollers","@willofdaedalus",[],{"time":255,"title":456,"type":247,"lead":234,"resources":457},"Product Demo: OpenMirror",[],{"time":459,"title":460,"type":247,"lead":234,"resources":461},"12:50 PM","Old Mutual",[462],{"title":463,"url":464},"Startup Resources","https://ecosystem.oldmutual.com.gh",{"time":466,"title":467,"type":247,"lead":234,"resources":468},"1:25 PM","FIDCON partnership announcement & promo code",[],{"time":470,"title":471,"type":247,"lead":234,"resources":472},"1:35 PM","Product Demo: JestryAI",[473],{"title":449,"url":474},"https://www.jestryai.com/",{"time":476,"title":477,"type":257,"lead":478,"resources":479},"1:50 PM","System Design Session (Blacko's Detty December Concert)","Tonny-Bright",[480],{"title":481,"url":482},"Problem Discription","https://docs.google.com/document/d/1qGhdc7L_PoCPuEGaheQ5RT5C-ML9-BzzfVQjmKTfSTQ/edit?usp=sharing",{"time":484,"title":485,"type":247,"lead":234,"resources":486},"2:50 PM","DevCongress Support Announcement",[487],{"title":488,"url":489},"Feedback Form","https://docs.google.com/forms/d/e/1FAIpQLSco-46QfOtkU70eOoFmskRa5TPLBGoVI34KudArxmrzO3dFdw/viewform",{"time":491,"title":492,"type":493,"lead":234,"resources":494},"3:10 PM","Panel Discussion - The New NITA Bill","panel",[495,498],{"title":496,"url":497},"NATIONAL INFORMATION TECHNOLOGY AUTHORITY BILL, 2025","https://nita.gov.gh/wp-content/uploads/2025/NITA-2008-act-2025-1.pdf",{"title":499,"url":500},"NITA REGULATORY | IT SERVICE PROVIDER REGISTRATION (FEES)","https://regulatory.nita.gov.gh/portal/regulatory/registration-of-it-firms",[502],{"url":503,"type":270},"https://drive.google.com/drive/folders/1NblGjqakAaTWZ7SBvTZBsImvnpQzhXrQ?usp=sharing",[],"content/meetups/500-may-2026.yaml","ba6e0c5996eadbfe","600-april-2026",{"id":507,"data":509,"filePath":548,"digest":549},{"name":510,"start":511,"end":512,"description":513,"cover":514,"location":515,"stream_url":234,"embed_stream":235,"registration_url":234,"speakers":517,"schedule":518,"photos":543,"videos":547},"DevCongress April Quarterly Meetup","2026-04-04T18:00:00+00:00","2026-04-04T21:45:00+00:00","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.\n","/images/quarterly-april-meet-up.jpeg",{"label":516,"name":516,"url":234},"Google Meet",[],[519,523,527,531,535,539],{"time":520,"title":521,"type":242,"lead":234,"resources":522},"6:00 PM","Greetings, weather check & community banter",[],{"time":524,"title":525,"type":257,"lead":234,"resources":526},"6:30 PM","What's happening in the tech ecosystem",[],{"time":528,"title":529,"type":257,"lead":234,"resources":530},"6:45 PM","Open discussion: AI, coding, hallucinations & Leetcode for AI",[],{"time":532,"title":533,"type":257,"lead":234,"resources":534},"8:00 PM","Community feedback: what DevCongress can improve",[],{"time":536,"title":537,"type":257,"lead":234,"resources":538},"8:45 PM","Funding monthly meetups and community support",[],{"time":540,"title":541,"type":242,"lead":234,"resources":542},"9:30 PM","Open banter & close",[],[544,545],{"url":514,"type":308},{"url":546,"type":308},"/images/quarterly-april-meetup-2.jpeg",[],"content/meetups/600-april-2026.yaml","573f1cd516539bb3","700-june-2026",{"id":550,"data":552,"filePath":606,"digest":607},{"name":553,"start":554,"end":555,"description":556,"cover":557,"location":558,"stream_url":234,"embed_stream":235,"registration_url":560,"speakers":561,"schedule":562,"photos":604,"videos":605},"DevCongress June Meetup","2026-06-20T10:00:00+00:00","2026-06-20T15:30:00+00:00","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.\n","https://images.lumacdn.com/uploads/cr/1a1af572-a563-4695-8382-2e3063121a4e.png",{"label":328,"name":329,"url":559},"https://www.google.com/maps/search/?api=1&query=5.5704282%2C-0.1888184","https://luma.com/uic56yjo",[],[563,566,571,576,580,585,589,594,599],{"time":564,"title":442,"type":247,"lead":234,"resources":565},"11:00 AM - 11:05 AM",[],{"time":567,"title":568,"type":247,"lead":569,"resources":570},"11:10 AM - 11:55 AM","Your fired employee still has access. Here is why.","Jesse",[],{"time":572,"title":573,"type":247,"lead":574,"resources":575},"12:00 PM - 12:30 PM","Selling SaaS to Ghanaian Businesses: What's Working So Far","Seth",[],{"time":577,"title":578,"type":257,"lead":234,"resources":579},"12:35 PM - 12:45 PM","Jampoll session",[],{"time":581,"title":582,"type":247,"lead":583,"resources":584},"12:45 PM - 1:30 PM","Design in the age of AI","Jeffrey Hinson",[],{"time":586,"title":587,"type":257,"lead":234,"resources":588},"1:35 PM - 2:35 PM","System Design session",[],{"time":590,"title":591,"type":247,"lead":592,"resources":593},"2:40 PM - 2:55 PM","QuiverFood demo","Christian",[],{"time":595,"title":596,"type":247,"lead":597,"resources":598},"3:00 PM - 3:15 PM","FlameLink demo","Codekeyz",[],{"time":600,"title":601,"type":247,"lead":602,"resources":603},"3:15 PM - 3:30 PM","Trospot demo","Joseph",[],[],[],"content/meetups/700-june-2026.yaml","af44f4e07586c56e","800-july-2026",{"id":608,"data":610,"filePath":622,"digest":623},{"name":611,"start":612,"end":613,"description":614,"cover":615,"location":616,"stream_url":234,"embed_stream":235,"registration_url":617,"speakers":618,"schedule":619,"photos":620,"videos":621},"DevCongress Quarterly Meetup [online]","2026-07-04T18:00:00+00:00","2026-07-04T21:00:00+00:00","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.\n","/images/quarterly-meetup-template.avif",{"label":516,"name":516,"url":617},"https://luma.com/1e9h6c34",[],[],[],[],"content/meetups/800-july-2026.yaml","691b5de188d7e826","meta::meta",["Map",626,627,628,629,630,631],"astro-config-digest","{\"root\":{},\"srcDir\":{},\"publicDir\":{},\"outDir\":{},\"cacheDir\":{},\"site\":\"https://devcongress.org\",\"compressHTML\":true,\"base\":\"/\",\"trailingSlash\":\"ignore\",\"output\":\"static\",\"scopedStyleStrategy\":\"attribute\",\"build\":{\"format\":\"directory\",\"client\":{},\"server\":{},\"assets\":\"_astro\",\"serverEntry\":\"entry.mjs\",\"redirects\":true,\"inlineStylesheets\":\"auto\",\"concurrency\":1},\"server\":{\"open\":false,\"host\":false,\"port\":4321,\"allowedHosts\":[]},\"redirects\":{},\"image\":{\"endpoint\":{\"route\":\"/_image\"},\"service\":{\"entrypoint\":\"astro/assets/services/sharp\",\"config\":{}},\"dangerouslyProcessSVG\":false,\"domains\":[],\"remotePatterns\":[],\"responsiveStyles\":false},\"devToolbar\":{\"enabled\":true},\"markdown\":{\"syntaxHighlight\":{\"type\":\"shiki\",\"excludeLangs\":[\"math\"]},\"shikiConfig\":{\"langs\":[],\"langAlias\":{},\"theme\":\"github-dark\",\"themes\":{},\"wrap\":false,\"transformers\":[]},\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{},\"processor\":{\"name\":\"unified\",\"options\":{\"remarkPlugins\":[],\"rehypePlugins\":[],\"remarkRehype\":{}}}},\"security\":{\"checkOrigin\":true,\"allowedDomains\":[],\"csp\":false,\"actionBodySizeLimit\":1048576,\"serverIslandBodySizeLimit\":1048576},\"env\":{\"schema\":{},\"validateSecrets\":false},\"prerenderConflictBehavior\":\"warn\",\"experimental\":{\"advancedRouting\":false,\"clientPrerender\":false,\"contentIntellisense\":false,\"chromeDevtoolsWorkspace\":false,\"rustCompiler\":false,\"queuedRendering\":{\"enabled\":false}},\"legacy\":{\"collectionsBackwardsCompat\":false}}","astro-version","6.4.2","content-config-digest","f98380167d899894","partners",["Map",634,635,643,644,652,653,661,662,670,671,679,680],"fido",{"id":634,"data":636,"filePath":641,"digest":642},{"name":637,"logo":638,"url":639,"backgroundColor":640},"Fido Credit","https://cdn.prod.website-files.com/62fa16d8d83816b0fc5defc4/62fcf5fdb02a38631df13edb_footer_logo.svg","https://gh.fido.money/","#d6086b","content/partners/fido.yaml","5b128ec983e6ea16","kweku_tech",{"id":643,"data":645,"filePath":650,"digest":651},{"name":646,"logo":647,"url":648,"backgroundColor":649},"Kweku Tech Media","https://www.kweku.tech/_next/image?url=%2Flogo.svg&w=256&q=75","https://kweku.tech","#070d0d","content/partners/kweku_tech.yaml","e8c7363657c5fc75","mest",{"id":652,"data":654,"filePath":659,"digest":660},{"name":655,"logo":656,"url":657,"backgroundColor":658},"MEST Africa","https://meltwater.org/wp-content/uploads/2024/01/MEST-Logo_landscape_03-e1704132329210-300x49.png","https://meltwater.org/","#ffffff","content/partners/mest.yaml","3ab53258ae155d60","old-mutual",{"id":661,"data":663,"filePath":668,"digest":669},{"name":664,"logo":665,"url":666,"backgroundColor":667},"Old Mutual Insurance Ltd.","https://xplorio.com/xplorio/categories/28627/OM_Logo_Horizonal_Reverse_on_Vignette_RGB_1583910489.jpg?width=960&height=440","https://www.oldmutual.com/","#60b84888","content/partners/old-mutual.yaml","340abccde4b94deb","paystack",{"id":670,"data":672,"filePath":677,"digest":678},{"name":673,"logo":674,"url":675,"backgroundColor":676},"Paystack","https://cdn-assets-cloud.frontify.com/s3/frontify-cloud-files-us/eyJwYXRoIjoiZnJvbnRpZnlcL2FjY291bnRzXC8yYVwvMTQxNzczXC9wcm9qZWN0c1wvMTc4NjE0XC9hc3NldHNcL2E5XC8zNTI0MTM2XC9jYmJkNzA1OTEyN2NlOGU3ZDI5ZDM5NmJkMzhmMzkwNi0xNTY5MjE3MjIzLnBuZyJ9:frontify:xd65O1_BCk88mGEkjq6maiXPQH5Ol5Vhn97tbY1vl0c?width=1600&format=webp&quality=100","https://paystack.com/","#f1fef4f2","content/partners/paystack.yaml","c589d5711904ae48","unicef",{"id":679,"data":681,"filePath":686,"digest":687},{"name":682,"logo":683,"url":684,"backgroundColor":685},"UNICEF Ghana","https://www.unicef.org/ghana/sites/unicef.org.ghana/files/styles/logo/public/English_23.png.webp?itok=TAurVHy1","https://www.unicef.org/ghana/","#008bbf","content/partners/unicef.yaml","eaf47158df9eab86","site",["Map",688,690],{"id":688,"data":691,"filePath":779},{"community_slack_url":692,"paystack_url":693,"youtube_embed_url":694,"stats":695,"socials":704,"roles":720,"mission_section":736,"donate_section":757,"hero":768},"https://devcongress-community.slack.com/join/shared_invite/zt-3elxv0f0y-U6JoK8Al4ExQp8ERaS8uwg#/shared-invite/email","https://paystack.shop/pay/devcongress-meetup","https://www.youtube-nocookie.com/embed/vNWWGWXjybg?rel=0&modestbranding=1&playsinline=1",[696,700],{"value":697,"label":698,"description":699},"6000+","Community Members","Developers, Designers, Founders, Hackers and Makers across Africa.",{"value":701,"label":702,"description":703},"10+","Years","Building Community since 2015.",[705,707,709,711,714,717],{"platform":141,"url":706},"https://www.linkedin.com/company/devcongress",{"platform":72,"url":708},"https://x.com/devcongress",{"platform":78,"url":710},"https://github.com/devcongress",{"platform":712,"url":713},"youtube","https://youtube.com/devcongress",{"platform":715,"url":716},"facebook","https://facebook.com/devcongress",{"platform":718,"url":719},"instagram","https://www.instagram.com/devcongress/",[721,722,723,724,725,726,727,728,729,730,731,732,733,734,735],"Software Engineer","Product Manager","UX Designer","Data Scientist","DevOps Engineer","Mobile Developer","Frontend Developer","Backend Engineer","QA Engineer","Tech Lead","Founder","Marketer","Legal Advisor","Sales Professional","System Architect",{"eyebrow":737,"headline":738,"body":739,"cta":740,"items":741},"Mission","We exist because Africa's builders deserve a community that takes them *seriously*.","DevCongress is a movement. No paywalls, no gatekeeping — just builders helping builders grow across Africa and beyond.","Join the community",[742,745,748,751,754],{"heading":743,"description":744},"Build capacity & confidence through community led *mentorship*.","Over 500+ professionals upskilled through mentorship, events, and hands-on feedback.",{"heading":746,"description":747},"Expose tech talents to a rich array of *resources*.","30+ community sessions, 100+ shared resources, and growing.",{"heading":749,"description":750},"Contribute to innovative and community-driven projects that create *real impact*.","Contributed to 10+ community-led tools powering innovation across Africa.",{"heading":752,"description":753},"Harness the collective strength of community to solve *real problems*.","From solo learners to startup founders, we connect and grow together.",{"heading":755,"description":756},"Build the *changemakers* who shape a nation's technology policy & future.","From community organising to national policy discussions, DevCongress members are agents of change — shaping the future of technology and society across Africa.",{"eyebrow":758,"heading":759,"body":760,"cta_text":761,"uses_header":762,"uses_items":763},"Support","Keep DevCongress running.","DevCongress is community-funded without the strings of corporate backing. Your contribution keeps the lights on and the community growing.","Donate via Paystack","Your support funds",[764,765,766,767],"Monthly meetups across Ghana","Dedicated Support Programs","Community Tools and Shared Resources","Critical Infrastructure to Run the Community",{"eyebrow":769,"headline":770,"cta_primary":771,"cta_secondary":774,"video_label":777,"video_live":778},"Africa's tech community","Where Africa's builders connect, collaborate and grow.",{"text":772,"color":773},"Join us on Slack","pink",{"text":775,"color":776},"Support the Mission","outline","dev:congress{}; — community highlight","● Live Community","content/site.yaml"] \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 75b12af..21c05e2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,6 +7,9 @@ on: pull_request: branches: - main + workflow_dispatch: + schedule: + - cron: '0 6 * * 1,4' permissions: contents: read diff --git a/README.md b/README.md index 817dd80..0fdcf6b 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,17 @@ All commands are run from the root of the project, from a terminal: | `pnpm astro ...` | Run CLI commands like `astro add`, `astro check` | | `pnpm astro -- --help` | Get help using the Astro CLI | +## Meetup data + +Meetup pages are built from the DevCongress Community public API by default: + +```txt +DEVCONGRESS_COMM_API_BASE_URL=https://devcongress-comm-api.elvis-yt211.workers.dev +DEVCONGRESS_COMM_ASSET_BASE_URL=https://devcon-comm.pages.dev +``` + +If the API cannot be reached during `pnpm build`, the site falls back to `content/meetups/*.yaml` so deploys do not fail because of a temporary API outage. + ## 👀 Want to learn more? Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/src/components/MeetupsSection.astro b/src/components/MeetupsSection.astro index 9ab7256..5df0806 100644 --- a/src/components/MeetupsSection.astro +++ b/src/components/MeetupsSection.astro @@ -1,20 +1,8 @@ --- -interface MeetupLocation { label?: string; name: string; url?: string | null; } -interface Meetup { - id: string; - data: { - name: string; - start: string; - end: string; - location: MeetupLocation; - description: string; - cover: string; - photos?: string[]; - registration_url?: string | null; - stream_url?: string | null; - }; -} +import type { WebsiteMeetup } from '../lib/meetups'; + interface Props { meetups: Meetup[]; } +type Meetup = WebsiteMeetup; const { meetups } = Astro.props; function getMeetupStatus(start: string, end: string) { @@ -39,8 +27,6 @@ const sorted = [...meetups] })); const recent = sorted.slice(0, 3); -const earlier = sorted.slice(3); - const statusConfig = { upcoming: { label: 'Upcoming', cta: 'Register →' }, live: { label: '● Live', cta: 'Follow live →' }, diff --git a/src/lib/meetups.ts b/src/lib/meetups.ts new file mode 100644 index 0000000..b0a0753 --- /dev/null +++ b/src/lib/meetups.ts @@ -0,0 +1,222 @@ +import { getCollection, type CollectionEntry } from 'astro:content'; + +const DEFAULT_API_BASE_URL = 'https://devcongress-comm-api.elvis-yt211.workers.dev'; +const DEFAULT_ASSET_BASE_URL = 'https://devcon-comm.pages.dev'; + +const apiBaseUrl = + import.meta.env.DEVCONGRESS_COMM_API_BASE_URL ?? + import.meta.env.PUBLIC_DEVCONGRESS_COMM_API_BASE_URL ?? + DEFAULT_API_BASE_URL; + +const assetBaseUrl = + import.meta.env.DEVCONGRESS_COMM_ASSET_BASE_URL ?? + import.meta.env.PUBLIC_DEVCONGRESS_COMM_ASSET_BASE_URL ?? + DEFAULT_ASSET_BASE_URL; + +let meetupsPromise: Promise | undefined; + +type LocalMeetup = CollectionEntry<'meetups'>; + +export interface MeetupLocation { + label?: string; + name: string; + url?: string | null; +} + +export interface MeetupPhoto { + url: string; + type?: 'image' | 'folder'; +} + +export interface MeetupSocial { + platform: 'x' | 'linkedin' | 'github' | 'website' | 'youtube' | 'instagram' | 'facebook' | 'discord' | 'slack'; + url: string; +} + +export interface MeetupSpeaker { + name: string; + title: string; + bio: string; + image: string; + talk_title: string; + talk_description: string; + slides_url?: string | null; + recording_url?: string | null; + socials?: MeetupSocial[]; +} + +export interface MeetupScheduleItem { + time: string; + title: string; + type: 'networking' | 'talk' | 'panel' | 'workshop' | 'open_discussion' | 'break'; + lead?: string | null; + resources?: Array<{ + title: string; + url: string; + }>; +} + +export interface MeetupVideo { + title: string; + embed_url: string; +} + +export interface WebsiteMeetup { + id: string; + data: { + name: string; + start: string; + end: string; + description: string; + cover: string; + location: MeetupLocation; + stream_url?: string | null; + embed_stream?: boolean; + registration_url?: string | null; + speakers?: MeetupSpeaker[]; + schedule?: MeetupScheduleItem[]; + photos?: MeetupPhoto[]; + videos?: MeetupVideo[]; + }; +} + +interface PublicMeetupsResponse { + data?: PublicMeetupDto[]; +} + +interface PublicMeetupDto { + id?: string; + slug?: string; + name?: string; + start?: string; + end?: string; + description?: string; + cover?: string; + location?: MeetupLocation; + stream_url?: string | null; + embed_stream?: boolean; + registration_url?: string | null; + speakers?: MeetupSpeaker[]; + schedule?: MeetupScheduleItem[]; + photos?: MeetupPhoto[]; + videos?: MeetupVideo[]; +} + +export async function getMeetups(): Promise { + meetupsPromise ??= loadMeetups(); + return meetupsPromise; +} + +export function sortMeetupsByNewest(meetups: WebsiteMeetup[]): WebsiteMeetup[] { + return [...meetups].sort((a, b) => new Date(b.data.start).getTime() - new Date(a.data.start).getTime()); +} + +async function loadMeetups(): Promise { + try { + const remoteMeetups = await fetchRemoteMeetups(); + if (remoteMeetups.length > 0) { + return remoteMeetups; + } + } catch (error) { + console.warn( + `[meetups] Falling back to local meetup YAML because ${getApiUrl('/api/public/meetups')} could not be loaded: ${getErrorMessage(error)}`, + ); + } + + return fetchLocalMeetups(); +} + +async function fetchRemoteMeetups(): Promise { + const response = await fetch(getApiUrl('/api/public/meetups'), { + headers: { accept: 'application/json' }, + }); + + if (!response.ok) { + throw new Error(`Meetups API returned ${response.status}`); + } + + const body = await response.json() as PublicMeetupsResponse; + if (!Array.isArray(body.data)) { + throw new Error('Meetups API response did not include a data array'); + } + + return body.data.map(mapPublicMeetup).filter((meetup): meetup is WebsiteMeetup => Boolean(meetup)); +} + +async function fetchLocalMeetups(): Promise { + const localMeetups = await getCollection('meetups'); + return localMeetups.map(mapLocalMeetup); +} + +function mapLocalMeetup(meetup: LocalMeetup): WebsiteMeetup { + return { + id: meetup.id, + data: { + ...meetup.data, + cover: resolveAssetUrl(meetup.data.cover, false), + speakers: meetup.data.speakers?.map((speaker) => ({ + ...speaker, + image: resolveAssetUrl(speaker.image, false), + })), + photos: meetup.data.photos?.map((photo) => ({ + ...photo, + url: resolveAssetUrl(photo.url, false), + })), + }, + }; +} + +function mapPublicMeetup(meetup: PublicMeetupDto): WebsiteMeetup | null { + if (!meetup.slug || !meetup.name || !meetup.start || !meetup.end || !meetup.description || !meetup.cover) { + return null; + } + + return { + id: meetup.slug, + data: { + name: meetup.name, + start: meetup.start, + end: meetup.end, + description: meetup.description, + cover: resolveAssetUrl(meetup.cover, true), + location: { + label: meetup.location?.label, + name: meetup.location?.name ?? 'Online', + url: meetup.location?.url ?? null, + }, + stream_url: meetup.stream_url ?? null, + embed_stream: meetup.embed_stream ?? false, + registration_url: meetup.registration_url ?? null, + speakers: meetup.speakers?.map((speaker) => ({ + ...speaker, + image: resolveAssetUrl(speaker.image, true), + })), + schedule: meetup.schedule ?? [], + photos: meetup.photos?.map((photo) => ({ + ...photo, + url: resolveAssetUrl(photo.url, true), + })), + videos: meetup.videos ?? [], + }, + }; +} + +function getApiUrl(path: string): string { + return new URL(path, ensureTrailingSlash(apiBaseUrl)).toString(); +} + +function resolveAssetUrl(url: string, fromRemote: boolean): string { + if (!fromRemote || !url.startsWith('/')) { + return url; + } + + return new URL(url, ensureTrailingSlash(assetBaseUrl)).toString(); +} + +function ensureTrailingSlash(url: string): string { + return url.endsWith('/') ? url : `${url}/`; +} + +function getErrorMessage(error: unknown): string { + return error instanceof Error ? error.message : String(error); +} diff --git a/src/pages/index.astro b/src/pages/index.astro index 06f9220..4e3bbb4 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -11,13 +11,14 @@ import MeetupsSection from '../components/MeetupsSection.astro'; import AdminsSection from '../components/AdminsSection.astro'; import PartnersSection from '../components/PartnersSection.astro'; import DonateSection from '../components/DonateSection.astro'; +import { getMeetups } from '../lib/meetups'; const siteEntries = await getCollection('site'); const site = siteEntries[0]!.data; const admins = await getCollection('admins'); const activities = await getCollection('activities'); const partners = await getCollection('partners'); -const meetups = await getCollection('meetups'); +const meetups = await getMeetups(); --- diff --git a/src/pages/meetups/[slug].astro b/src/pages/meetups/[slug].astro index d9849a9..3559968 100644 --- a/src/pages/meetups/[slug].astro +++ b/src/pages/meetups/[slug].astro @@ -1,9 +1,9 @@ --- -import { getCollection } from 'astro:content'; import Base from '../../layouts/Base.astro'; +import { getMeetups } from '../../lib/meetups'; export async function getStaticPaths() { - const meetups = await getCollection('meetups'); + const meetups = await getMeetups(); return meetups.map((meetup) => ({ params: { slug: meetup.id }, props: { meetup }, diff --git a/src/pages/meetups/index.astro b/src/pages/meetups/index.astro index 469ecce..46f6afc 100644 --- a/src/pages/meetups/index.astro +++ b/src/pages/meetups/index.astro @@ -1,8 +1,8 @@ --- -import { getCollection } from 'astro:content'; import Base from '../../layouts/Base.astro'; +import { getMeetups, sortMeetupsByNewest } from '../../lib/meetups'; -const meetups = await getCollection('meetups'); +const meetups = await getMeetups(); function getMeetupStatus(start: string, end: string) { const now = new Date(); @@ -17,8 +17,7 @@ function formatDate(iso: string): string { }); } -const sorted = [...meetups] - .sort((a, b) => new Date(b.data.start).getTime() - new Date(a.data.start).getTime()) +const sorted = sortMeetupsByNewest(meetups) .map((m) => ({ ...m, slug: m.id,