Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
31 changes: 31 additions & 0 deletions app/(app)/speakers/_client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { SpeakerForm } from "@/components/Speaker/SpeakerForm";

export function SpeakersClient() {
return (
<div className="bg-black">
<div className="mx-auto max-w-3xl px-4 py-16 sm:px-6 sm:py-24 lg:px-8">
<header className="mb-10">
<p className="mb-4 inline-block rounded-full bg-gradient-to-r from-orange-400/20 to-pink-600/20 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-orange-300">
Speak at Codú
</p>
<h1 className="text-3xl font-extrabold tracking-tight text-white sm:text-4xl">
Pitch a talk at a Codú meetup
</h1>
<p className="mt-4 text-lg leading-relaxed text-neutral-300">
Codú runs regular meetups across Ireland and we&apos;re always
looking for speakers. Whether it&apos;s your first talk or your
fiftieth, we&apos;d love to hear your pitch. Propose a talk (or up
to three) and we&apos;ll be in touch.
</p>
</header>

<SpeakerForm />

<p className="mt-6 text-center text-sm text-neutral-400">
Takes about 3 minutes. First-time speakers welcome — we&apos;ll help
you prep.
</p>
</div>
</div>
);
}
69 changes: 69 additions & 0 deletions app/(app)/speakers/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { Metadata } from "next";
import { JsonLd } from "@/components/JsonLd";
import { SpeakersClient } from "./_client";

const PAGE_URL = "https://www.codu.co/speakers";
const PAGE_TITLE = "Speak at Codú — Pitch a Talk for Our Meetups";
const PAGE_DESCRIPTION =
"Pitch a talk at a Codú meetup. First-time speakers welcome. We run regular developer meetups across Ireland and are always looking for people to share what they've built, learned, or broken.";

export const metadata: Metadata = {
title: PAGE_TITLE,
description: PAGE_DESCRIPTION,
keywords: [
"Codú speaker",
"tech meetup speaker Ireland",
"developer meetup Dublin",
"first time speaker",
"web development talk",
"speak at meetup Ireland",
],
alternates: { canonical: PAGE_URL },
robots: { index: true, follow: true },
openGraph: {
title: "Speak at Codú",
description: PAGE_DESCRIPTION,
url: PAGE_URL,
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Speak at Codú",
description: PAGE_DESCRIPTION,
},
};

const speakerJsonLd = {
"@context": "https://schema.org",
"@type": "JobPosting",
title: "Speaker — Codú Meetups",
description: PAGE_DESCRIPTION,
employmentType: "VOLUNTEER",
hiringOrganization: {
"@type": "Organization",
name: "Codú",
sameAs: "https://www.codu.co",
logo: "https://www.codu.co/images/codu-logo.png",
},
jobLocation: {
"@type": "Place",
address: {
"@type": "PostalAddress",
addressLocality: "Dublin",
addressCountry: "IE",
},
},
applicantLocationRequirements: { "@type": "Country", name: "Worldwide" },
jobLocationType: "TELECOMMUTE",
datePosted: new Date().toISOString().split("T")[0],
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
url: PAGE_URL,
};

export default function SpeakersPage() {
return (
<>
<JsonLd data={speakerJsonLd} />
<SpeakersClient />
</>
);
}
31 changes: 31 additions & 0 deletions app/(app)/volunteer/_client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { VolunteerForm } from "@/components/Volunteer/VolunteerForm";

export function VolunteerClient() {
return (
<div className="bg-black">
<div className="mx-auto max-w-3xl px-4 py-16 sm:px-6 sm:py-24 lg:px-8">
<header className="mb-10">
<p className="mb-4 inline-block rounded-full bg-gradient-to-r from-orange-400/20 to-pink-600/20 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-orange-300">
Volunteer with Codú
</p>
<h1 className="text-3xl font-extrabold tracking-tight text-white sm:text-4xl">
Help us build Ireland&apos;s largest web dev community
</h1>
<p className="mt-4 text-lg leading-relaxed text-neutral-300">
Codú is Ireland&apos;s largest web dev community — thousands of
developers, regular meetups, and a newsletter across the Irish tech
ecosystem. We&apos;re opening volunteer spots for people interested
in marketing and events.
</p>
</header>

<VolunteerForm />

<p className="mt-6 text-center text-sm text-neutral-400">
Takes about 3 minutes. We read every application and reply within 2
weeks.
</p>
</div>
</div>
);
}
69 changes: 69 additions & 0 deletions app/(app)/volunteer/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import type { Metadata } from "next";
Comment thread
coderabbitai[bot] marked this conversation as resolved.
import { JsonLd } from "@/components/JsonLd";
import { VolunteerClient } from "./_client";

const PAGE_URL = "https://www.codu.co/volunteer";
const PAGE_TITLE = "Volunteer with Codú — Help Build Ireland's Largest Dev Community";
const PAGE_DESCRIPTION =
"Join the team behind Codú. We're recruiting volunteer marketers and event organisers to help run meetups, newsletters, partnerships, and socials across the Irish tech ecosystem.";

export const metadata: Metadata = {
title: PAGE_TITLE,
description: PAGE_DESCRIPTION,
keywords: [
"Codú volunteer",
"volunteer developer community",
"Ireland tech community",
"web developer volunteer",
"tech meetup organiser Ireland",
"marketing volunteer",
"events volunteer",
],
alternates: { canonical: PAGE_URL },
robots: { index: true, follow: true },
openGraph: {
title: "Volunteer with Codú",
description: PAGE_DESCRIPTION,
url: PAGE_URL,
type: "website",
},
twitter: {
card: "summary_large_image",
title: "Volunteer with Codú",
description: PAGE_DESCRIPTION,
},
};

const volunteerJsonLd = {
"@context": "https://schema.org",
"@type": "JobPosting",
title: "Volunteer — Marketing & Events",
description: PAGE_DESCRIPTION,
employmentType: "VOLUNTEER",
hiringOrganization: {
"@type": "Organization",
name: "Codú",
sameAs: "https://www.codu.co",
logo: "https://www.codu.co/images/codu-logo.png",
},
jobLocation: {
"@type": "Place",
address: {
"@type": "PostalAddress",
addressCountry: "IE",
},
},
applicantLocationRequirements: { "@type": "Country", name: "Worldwide" },
jobLocationType: "TELECOMMUTE",
datePosted: new Date().toISOString().split("T")[0],
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and read the file
find . -name "page.tsx" -path "*/volunteer/*" -type f

Repository: codu-code/codu

Length of output: 88


🏁 Script executed:

# Get more context around lines 56-58
sed -n '50,65p' "app/(app)/volunteer/page.tsx" | cat -n

Repository: codu-code/codu

Length of output: 563


🌐 Web query:

Google JobPosting structured data requirements applicantLocationRequirements

💡 Result:

Google's requirements for the applicantLocationRequirements property in JobPosting structured data are as follows: The property specifies the geographic location(s) from which applicants can apply, primarily for telecommuting or remote jobs. It is of type AdministrativeArea (e.g., Country, State) according to Schema.org. Key guidelines from official Google documentation: - Use it with jobLocationType: "TELECOMMUTE" for remote jobs. - At least one country is required for work-from-home jobs. - If omitted for remote jobs, Google defaults to the country in jobLocation. - Do not use for citizenship or work visa requirements (use eligibilityToWorkRequirement instead). - Can be an array for multiple locations. - Example: {"@type": "Country", "name": "USA"} It is recommended (not strictly required) for rich results eligibility, but essential for correct display of remote job restrictions to avoid validation errors in Search Console. Examples from docs: { "applicantLocationRequirements": { "@type": "Country", "name": "USA" }, "jobLocationType": "TELECOMMUTE" }

Citations:


🌐 Web query:

schema.org JobPosting Country applicantLocationRequirements valid values

💡 Result:

The schema.org JobPosting property applicantLocationRequirements specifies the location(s) from which applicants can apply, typically for telecommute jobs. Its expected type is AdministrativeArea, which includes Country as a subtype (since Country extends AdministrativeArea). When using Country for applicantLocationRequirements, valid values are instances of the Country type. There is no fixed enumerated list of valid countries; instead, use: - @type: "Country" - name: The country name (e.g., "USA") - Optionally, sameAs: A URL to an authoritative identifier like Wikidata (e.g., "https://www.wikidata.org/wiki/Q30" for USA) Examples from official schema.org documentation: { "@context": "https://schema.org/", "@type": "JobPosting", "name": "Telecommute from anywhere in USA!", "applicantLocationRequirements": { "@type": "Country", "sameAs": "https://www.wikidata.org/wiki/Q30", "name": "USA" } } Google's documentation confirms this usage for remote jobs, requiring at least one country. Note: Some older docs list only AdministrativeArea, but examples and current usage (v29.4+) support Country. It should not be used for citizenship/visa requirements (use eligibilityToWorkRequirement instead). Country itself follows ISO 3166 conventions (e.g., 2-letter codes in related properties like addressCountry), but for this property, structured markup with name and sameAs is preferred over codes.

Citations:


Use concrete JobPosting location metadata and a fixed posting date.

Worldwide is not a valid country value for applicantLocationRequirements. Google's JobPosting guidance requires at least one concrete country for remote roles (e.g., "USA", "Ireland"). Additionally, datePosted should not be dynamically regenerated on every build—it should be a fixed date representing when the posting actually went live. Google's documentation states the posting date should be genuine and only change when the job posting is actually updated.

Given the address uses addressCountry: "IE", the proposed fix using "Ireland" as the concrete location is appropriate:

Proposed fix
+const VOLUNTEER_POSTED_DATE = "2026-04-21";
+
 const volunteerJsonLd = {
   "@context": "https://schema.org",
   "@type": "JobPosting",
@@
-  applicantLocationRequirements: { "@type": "Country", name: "Worldwide" },
+  applicantLocationRequirements: { "@type": "Country", name: "Ireland" },
   jobLocationType: "TELECOMMUTE",
-  datePosted: new Date().toISOString().split("T")[0],
+  datePosted: VOLUNTEER_POSTED_DATE,
   url: PAGE_URL,
 };

If applicants are genuinely eligible from multiple countries, enumerate those countries explicitly and ensure the visible page copy clearly states the 100% remote arrangement.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
applicantLocationRequirements: { "@type": "Country", name: "Worldwide" },
jobLocationType: "TELECOMMUTE",
datePosted: new Date().toISOString().split("T")[0],
const VOLUNTEER_POSTED_DATE = "2026-04-21";
const volunteerJsonLd = {
"@context": "https://schema.org",
"@type": "JobPosting",
applicantLocationRequirements: { "@type": "Country", name: "Ireland" },
jobLocationType: "TELECOMMUTE",
datePosted: VOLUNTEER_POSTED_DATE,
url: PAGE_URL,
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/`(app)/volunteer/page.tsx around lines 56 - 58, Replace the invalid
"Worldwide" value in the JobPosting metadata by using a concrete country
matching the address (use "Ireland" for applicantLocationRequirements.name since
addressCountry is "IE"), and stop regenerating datePosted at build time — set
datePosted to a fixed ISO date string representing when the posting went live
(e.g., "2026-04-01"); update the applicantLocationRequirements and datePosted
fields in the JobPosting object in page.tsx (look for
applicantLocationRequirements, jobLocationType, datePosted) and if the role
truly accepts multiple countries, explicitly list each country name in
applicantLocationRequirements instead of a generic value.

url: PAGE_URL,
};

export default function VolunteerPage() {
return (
<>
<JsonLd data={volunteerJsonLd} />
<VolunteerClient />
</>
);
}
2 changes: 2 additions & 0 deletions app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const ROUTES_TO_INDEX = [
"/feed",
"/advertise",
"/code-of-conduct",
"/volunteer",
"/speakers",
];

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
Expand Down
Loading
Loading