Skip to content

Commit 0fe667b

Browse files
JeanneGrenetyoannfleurydev
authored andcommitted
feat: fist version of the subpage
1 parent 3ac6a7e commit 0fe667b

File tree

7 files changed

+290
-3
lines changed

7 files changed

+290
-3
lines changed

src/components/OrganizersList/index.astro

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,25 @@ interface Props {
1111
}
1212
1313
const { organizers, title = "Organized by" } = Astro.props;
14+
15+
const { countryId } = Astro.params;
1416
---
1517

1618
{
1719
organizers.length > 0 && (
1820
<div class="flex flex-col gap-3 md:max-w-xs">
19-
<h3 class="text-xs font-medium uppercase tracking-widest opacity-60">
20-
{title}
21-
</h3>
21+
<a
22+
href={lunalink(
23+
ROUTES["events"].locations[":countryId"]["organizers"].__path,
24+
{
25+
countryId: countryId ?? "",
26+
},
27+
)}
28+
>
29+
<h3 class="text-xs font-medium uppercase tracking-widest opacity-60">
30+
{title}
31+
</h3>
32+
</a>
2233
<div class="flex flex-wrap gap-2">
2334
{organizers.map((organizer) => (
2435
<a
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
import { Image } from "astro:assets";
3+
import SpeakerPlaceholder from "@/assets/images/people-placeholder.jpeg";
4+
import type { CollectionEntry } from "astro:content";
5+
import { lunalink } from "@bearstudio/lunalink";
6+
import { ROUTES } from "@/routes.gen";
7+
import { SOCIALS_TYPE_MAP } from "@/content/socials";
8+
import { render } from "astro:content";
9+
import Prose from "@/components/Prose/index.astro";
10+
11+
interface Props {
12+
organizer: CollectionEntry<"people">;
13+
}
14+
15+
const { organizer } = Astro.props;
16+
17+
const { Content } = await render(organizer);
18+
---
19+
20+
<div class="group flex h-fit w-fit items-start gap-4">
21+
<a
22+
href={lunalink(ROUTES.people[":id"].__path, {
23+
id: organizer.id,
24+
})}
25+
>
26+
<div class="aspect-square overflow-hidden rounded-lg">
27+
<Image
28+
src={organizer.data.avatar ?? SpeakerPlaceholder}
29+
alt={organizer.data.name}
30+
width={160}
31+
height={160}
32+
/>
33+
</div>
34+
</a>
35+
<div class="flex flex-col gap-2">
36+
<a
37+
href={lunalink(ROUTES.people[":id"].__path, {
38+
id: organizer.id,
39+
})}
40+
class="flex flex-col gap-0.5"
41+
>
42+
<p
43+
class="line-clamp-3 font-heading text-xl font-medium tracking-wide text-primary"
44+
>
45+
{organizer.data.name}
46+
</p>
47+
{
48+
!!organizer.data.job && (
49+
<p class="line-clamp-3 text-sm tracking-wide opacity-60">
50+
{organizer.data.job}
51+
</p>
52+
)
53+
}
54+
</a>
55+
56+
{
57+
!!organizer.data.socials && (
58+
<ul class="flex flex-row gap-4">
59+
{organizer.data.socials.map((social) => {
60+
const Icon = SOCIALS_TYPE_MAP[social.type];
61+
62+
return (
63+
<li>
64+
<a
65+
href={social.href}
66+
class="text-lg opacity-60 transition hover:text-primary hover:opacity-100"
67+
target="_blank"
68+
>
69+
<span class="sr-only">{social.type}</span>
70+
<Icon />
71+
</a>
72+
</li>
73+
);
74+
})}
75+
</ul>
76+
)
77+
}
78+
<Prose class="prose-sm hidden md:block">
79+
<Content />
80+
</Prose>
81+
</div>
82+
</div>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { apiImageEndpoint } from "@/generated-assets/api";
2+
import type { APIRoute } from "astro";
3+
4+
export const prerender = false;
5+
6+
export const GET: APIRoute = apiImageEndpoint(
7+
import.meta.glob("./_*.tsx", { eager: true }),
8+
);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Frame } from "@/generated-assets/components/Frame";
2+
import {
3+
getAstroImageBase64,
4+
type AssetImageConfig,
5+
} from "@/generated-assets/image";
6+
import { BgImage } from "@/generated-assets/components/BgImage";
7+
import { Logo } from "@/components/Logo";
8+
import { getCountryData } from "@/pages/events/locations/[countryId]/[cityId]/assets/_utils";
9+
import type { ExtractParams } from "@bearstudio/lunalink";
10+
import type { ROUTES } from "@/routes.gen";
11+
import { OG_IMAGE } from "@/assets/consts";
12+
13+
export const config: AssetImageConfig = OG_IMAGE;
14+
15+
export default async function ({
16+
params,
17+
}: {
18+
params: ExtractParams<
19+
(typeof ROUTES.events.locations)[":countryId"]["organizers"]["__path"]
20+
>;
21+
}) {
22+
const country = await getCountryData(params.countryId);
23+
const postCover = await getAstroImageBase64(country.data.cover.media);
24+
25+
return (
26+
<Frame {...config} style={{ padding: 128 }}>
27+
<BgImage src={postCover} width={config.width} height={config.height} />
28+
29+
<div
30+
style={{
31+
display: "flex",
32+
flexDirection: "column",
33+
gap: 128,
34+
width: "100%",
35+
justifyContent: "center",
36+
zIndex: 100,
37+
}}
38+
>
39+
<Logo style={{ width: 169 * 3, height: 18 * 3 }} />
40+
41+
<div
42+
style={{
43+
display: "flex",
44+
flexDirection: "column",
45+
gap: 20,
46+
}}
47+
>
48+
<div
49+
style={{
50+
display: "flex",
51+
fontSize: 148,
52+
fontWeight: 500,
53+
marginLeft: -6, // Visual alignment
54+
}}
55+
>
56+
{country.data.name}
57+
</div>
58+
<div
59+
style={{
60+
display: "flex",
61+
fontSize: 80,
62+
fontWeight: 500,
63+
opacity: 0.6,
64+
marginTop: -16,
65+
marginLeft: -2, // Visual alignment
66+
}}
67+
>
68+
All the organizers in {country.data.name}
69+
</div>
70+
</div>
71+
</div>
72+
</Frame>
73+
);
74+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { NotFoundAssetError } from "@/generated-assets/api";
2+
import { getEntry } from "astro:content";
3+
4+
export const getCountryData = async (id: string) => {
5+
const country = await getEntry("countries", id);
6+
7+
if (!country) {
8+
throw new NotFoundAssetError();
9+
}
10+
11+
return country;
12+
};
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
---
2+
import MainLayout from "@/layouts/MainLayout.astro";
3+
import SEO from "@/components/SEO/index.astro";
4+
import { getEntry } from "astro:content";
5+
import { getCollection } from "astro:content";
6+
import ImageBackgroundHero from "@/components/ImageBackgroundHero/index.astro";
7+
import { getPeopleFromReference } from "@/lib/people";
8+
import { BackButton } from "@/components/BackButton";
9+
import { lunalink } from "@bearstudio/lunalink";
10+
import { ROUTES } from "@/routes.gen";
11+
import PeopleWithDescription from "@/components/PeopleWithDescription/index.astro";
12+
13+
export async function getStaticPaths() {
14+
const countries = await getCollection("countries");
15+
16+
return countries.map((country) => ({
17+
params: { countryId: country.id },
18+
}));
19+
}
20+
const { countryId } = Astro.params;
21+
22+
const country = await getEntry("countries", countryId);
23+
24+
if (!country) {
25+
return new Response(null, {
26+
status: 404,
27+
statusText: "Not found",
28+
});
29+
}
30+
31+
const ogImage = new URL(
32+
Astro.url.pathname + "/assets/og-image.jpg",
33+
Astro.site,
34+
);
35+
36+
const organizers = await getPeopleFromReference(country.data.organizers ?? []);
37+
---
38+
39+
<MainLayout>
40+
<SEO
41+
slot="seo"
42+
title={`All the organizers in ${country.data.name}`}
43+
description={country.data.description ?? ""}
44+
image={ogImage.toString()}
45+
/>
46+
<ImageBackgroundHero
47+
src={country.data.cover.media}
48+
alt={country.data.cover.alt}
49+
/>
50+
<div class="mx-auto w-full max-w-screen-lg px-4 py-4">
51+
<BackButton
52+
href={lunalink(ROUTES["events"].locations[":countryId"].__path, {
53+
countryId,
54+
})}
55+
client:load
56+
contextLabel={`Fork It! Events in ${country.data.name}`}
57+
/>
58+
<div
59+
class="flex w-full flex-col justify-between gap-8 py-12 md:min-h-[40svh] md:flex-row"
60+
>
61+
<div class="flex flex-1 flex-col gap-8">
62+
<div class="flex w-full flex-col gap-2">
63+
<h1
64+
class="w-full text-balance text-left font-heading text-4xl font-medium uppercase tracking-wider md:text-5xl"
65+
>
66+
{country.data.name}
67+
</h1>
68+
<h2
69+
class="w-full text-balance text-left text-base tracking-widest md:text-lg"
70+
>
71+
All the Fork it! events organizers in {country.data.name}
72+
</h2>
73+
<p
74+
class="max-w-[60ch] text-sm tracking-wide [text-shadow:0_2px_30px_rgba(0,0,0,0.4)]"
75+
>
76+
Fork it! Community is the result of the hard work of four
77+
experienced professionals who have combined their strengths and
78+
skills to create a dynamic and innovative community. This community
79+
is determined to share real experiences, inspire innovation and
80+
connect creative minds in the tech world.
81+
</p>
82+
</div>
83+
<div class="flex flex-col gap-6">
84+
{
85+
organizers.map((organizer) => (
86+
<PeopleWithDescription organizer={organizer} />
87+
))
88+
}
89+
</div>
90+
</div>
91+
</div>
92+
</div>
93+
</MainLayout>

src/routes.gen.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ const ROUTES_CONFIG = {
9090
":__image.:__type": {},
9191
"_og-image": {},
9292
"_utils": {}
93+
},
94+
"organizers": {
95+
"assets": {
96+
":__image.:__type": {},
97+
"_og-image": {},
98+
"_utils": {}
99+
}
93100
}
94101
}
95102
},

0 commit comments

Comments
 (0)