Skip to content

Commit cc76563

Browse files
bjgavilanesnsandoyaIvanM9
authored
Community and Event card components #9 (#17) (#19)
* Community and Event card components #9 (#17) * Create CardCommunity.astro component (including interfaces and implementation in community page) * Create CardEvent.astro component (including interfaces and implementation in events page) * Save interfaces in separated files * Update src/interfaces/events.ts Co-authored-by: Iván Manzaba <[email protected]> * Create CardBase, refactor CardEvent and CardCommunity --------- Co-authored-by: Iván Manzaba <[email protected]> * chore: add project name to package.json * feat!: pnpm install * feat!(nodejs): corepack use pnpm * feat: add sitemap integration to Astro configuration * feat: add robots.txt file with sitemap integration * feat: Add RSS support with new rss.xml page and link in footer * feat: Integrate astro-compressor into Astro configuration * fix(misc): dos2unix conversion Replace CR/LF sequence into UNIX LF using dos2unix. Command used: `find . -type f -exec dos2unix {} \+` This is a non-issue. * feat!: erase package-lock.json * feat: add sitemap integration to Astro configuration * feat: Add RSS support with new rss.xml page and link in footer * feat: Integrate astro-compressor into Astro configuration --------- Co-authored-by: Nataly Sandoya <[email protected]> Co-authored-by: Iván Manzaba <[email protected]> Co-authored-by: Iván Manzaba <[email protected]>
1 parent 285d956 commit cc76563

File tree

9 files changed

+199
-150
lines changed

9 files changed

+199
-150
lines changed

astro.config.mjs

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,4 @@ import compressor from 'astro-compressor';
1212
// https://astro.build/config
1313
export default defineConfig({
1414
site: "https://ecuadorintech.org",
15-
integrations: [tailwind(), alpinejs(), sitemap(), compressor()]
16-
});
15+
integrations: [tailwind(), alpinejs(), sitemap(), compressor()]

src/assets/events.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@
1111
"image": "/github-workshop.jpg",
1212
"attendees": 31
1313
}
14-
]
15-
14+
]

src/components/CardBase.astro

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
interface Props {
3+
title: string,
4+
description: string,
5+
image: string,
6+
modality?: string,
7+
}
8+
9+
const {title, image, modality, description} = Astro.props
10+
---
11+
<div
12+
class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300 cursor-pointer"
13+
>
14+
<div class="h-48 overflow-hidden relative">
15+
<img
16+
src={image}
17+
alt={title}
18+
class="w-full h-full object-cover transform hover:scale-105 transition-transform duration-300"
19+
/>
20+
{modality && <div class="absolute top-4 right-4">
21+
<span
22+
class={`px-3 py-1 rounded-full text-sm font-semibold ${
23+
modality === "Hybrid"
24+
? "bg-purple-100 text-purple-800"
25+
: modality === "Virtual"
26+
? "bg-green-100 text-green-800"
27+
: "bg-blue-100 text-blue-800"
28+
}`}
29+
>
30+
{modality}
31+
</span>
32+
</div>}
33+
</div>
34+
<div class="p-6">
35+
<!-- Title and description -->
36+
<div class="flex flex-col">
37+
<div class="flex justify-between items-start mb-3">
38+
<h2 class="text-xl font-bold text-gray-800">{title}</h2>
39+
</div>
40+
<p class="text-gray-600 mb-4 line-clamp-2">{description}</p>
41+
</div>
42+
<!-- Custom info -->
43+
<slot />
44+
</div>
45+
</div>
46+

src/components/CardCommunity.astro

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
import { object } from 'astro:schema';
3+
import type {Community, Links} from '../interfaces/communities'
4+
import CardBase from './CardBase.astro';
5+
6+
const {name, description, links, tags=[], image} = Astro.props as Community
7+
const communityHeaderProps = {title:name, image, description}
8+
9+
---
10+
<CardBase {...communityHeaderProps}>
11+
<!-- Tags -->
12+
<div class="mb-4 flex flex-wrap gap-2">
13+
{tags.map((tag) => (
14+
<span class="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-sm font-medium hover:bg-gray-200 transition-colors">
15+
{tag}
16+
</span>
17+
))}
18+
</div>
19+
<!-- Social Media -->
20+
<div class="pt-4 border-t border-gray-100">
21+
<div class="flex gap-3">
22+
{links &&
23+
Object.entries(links).map(([key, value]) => {
24+
// const hostname = new URL(link).hostname;
25+
// const icon = hostname.includes("instagram")
26+
// ? "💬"
27+
// : hostname.includes("github")
28+
// ? "📚"
29+
// : hostname.includes("linkedin")
30+
// ? "🎨"
31+
// : "🔗";
32+
return (
33+
<a
34+
href={value}
35+
class="inline-flex items-center gap-1 text-blue-600 hover:text-blue-800 transition-colors text-sm"
36+
target="_blank"
37+
rel="noopener noreferrer"
38+
>
39+
<span>{key}</span>
40+
{/* {hostname.split(".")[0]} */}
41+
</a>
42+
);
43+
})}
44+
</div>
45+
</div>
46+
</CardBase>

src/components/CardEvent.astro

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
---
2+
import type { Event } from "../interfaces/events"
3+
import CardBase from "./CardBase.astro"
4+
5+
const {title, description, link, date, place, modality, hostedBy, image, attendees} = Astro.props as Event
6+
7+
const eventHeaderProps = {title, image, modality, description}
8+
---
9+
<CardBase {...eventHeaderProps}>
10+
<!-- Event Date, Location and Atendees -->
11+
<div class="space-y-2 text-sm text-gray-500">
12+
<div class="flex items-center gap-2">
13+
<svg
14+
class="w-4 h-4"
15+
fill="none"
16+
stroke="currentColor"
17+
viewBox="0 0 24 24"
18+
>
19+
<path
20+
stroke-linecap="round"
21+
stroke-linejoin="round"
22+
stroke-width="2"
23+
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
24+
/>
25+
</svg>
26+
<span>
27+
{new Date(date).toLocaleDateString("en-US", {
28+
weekday: "long",
29+
year: "numeric",
30+
month: "long",
31+
day: "numeric",
32+
})}
33+
</span>
34+
</div>
35+
<div class="flex items-center gap-2">
36+
<svg
37+
class="w-4 h-4"
38+
fill="none"
39+
stroke="currentColor"
40+
viewBox="0 0 24 24"
41+
>
42+
<path
43+
stroke-linecap="round"
44+
stroke-linejoin="round"
45+
stroke-width="2"
46+
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
47+
/>
48+
<path
49+
stroke-linecap="round"
50+
stroke-linejoin="round"
51+
stroke-width="2"
52+
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
53+
/>
54+
</svg>
55+
<span>{place}</span>
56+
</div>
57+
<div class="flex items-center gap-2">
58+
<svg
59+
class="w-4 h-4"
60+
fill="none"
61+
stroke="currentColor"
62+
viewBox="0 0 24 24"
63+
>
64+
<path
65+
stroke-linecap="round"
66+
stroke-linejoin="round"
67+
stroke-width="2"
68+
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
69+
/>
70+
</svg>
71+
<span>{attendees} attendees</span>
72+
</div>
73+
</div>
74+
</CardBase>

src/interfaces/communities.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export interface Community {
2+
name: string,
3+
description: string,
4+
links: Links,
5+
tags: string[],
6+
image: string
7+
}
8+
9+
export interface Links{
10+
website: string,
11+
social: string
12+
}

src/interfaces/events.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export interface Event {
2+
title: string,
3+
description: string,
4+
link: string,
5+
date: string,
6+
place: string,
7+
modality: string,
8+
hostedBy: string,
9+
image: string,
10+
attendees: number
11+
}

src/pages/communities.astro

+4-52
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
---
22
import Layout from '../layouts/Layout.astro';
33
import communities from '../assets/communities.json'
4+
import type { Community } from '../interfaces/communities';
5+
import CardCommunity from '../components/CardCommunity.astro';
46
57
---
68

@@ -9,58 +11,8 @@ import communities from '../assets/communities.json'
911
<h1 class="text-4xl font-bold mb-8">Comunidades</h1>
1012
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
1113
{
12-
communities.map((community) => (
13-
<div class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300">
14-
<div class="h-48 overflow-hidden">
15-
<img
16-
src={community.image}
17-
alt={community.name}
18-
class="w-full h-full object-cover transform hover:scale-105 transition-transform duration-300"
19-
/>
20-
</div>
21-
<div class="p-6">
22-
<div class="flex justify-between items-center mb-3">
23-
<h2 class="text-xl font-bold text-gray-800">
24-
{community.name}
25-
</h2>
26-
</div>
27-
<p class="text-gray-600 mb-4 line-clamp-2">
28-
{community.description}
29-
</p>
30-
<div class="mb-4 flex flex-wrap gap-2">
31-
{community.tags.map((tag) => (
32-
<span class="bg-gray-100 text-gray-700 px-3 py-1 rounded-full text-sm font-medium hover:bg-gray-200 transition-colors">
33-
{tag}
34-
</span>
35-
))}
36-
</div>
37-
<div class="pt-4 border-t border-gray-100">
38-
<div class="flex gap-3">
39-
{Object.entries(community.links).map(([key, value]) => {
40-
// const hostname = new URL(link).hostname;
41-
// const icon = hostname.includes("instagram")
42-
// ? "💬"
43-
// : hostname.includes("github")
44-
// ? "📚"
45-
// : hostname.includes("linkedin")
46-
// ? "🎨"
47-
// : "🔗";
48-
return (
49-
<a
50-
href={value}
51-
class="inline-flex items-center gap-1 text-blue-600 hover:text-blue-800 transition-colors text-sm"
52-
target="_blank"
53-
rel="noopener noreferrer"
54-
>
55-
<span>{key}</span>
56-
{/* {hostname.split(".")[0]} */}
57-
</a>
58-
);
59-
})}
60-
</div>
61-
</div>
62-
</div>
63-
</div>
14+
communities.map((community:Community) => (
15+
<CardCommunity {...community} />
6416
))
6517
}
6618
</div>

src/pages/events.astro

+4-94
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,17 @@
11
---
22
import Layout from '../layouts/Layout.astro'
33
import events from '../assets/events.json'
4+
import CardEvent from '../components/CardEvent.astro'
5+
import type { Event } from '../interfaces/events'
46
---
57

68
<Layout title="Events - Ecuador In Tech">
79
<div class="container mx-auto px-4 py-12" x-data="{ selectedEvent: null }">
810
<h1 class="text-4xl font-bold mb-8">Eventos</h1>
911
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
1012
{
11-
events.map((event, index) => (
12-
<div
13-
class="bg-white rounded-xl shadow-lg overflow-hidden hover:shadow-xl transition-shadow duration-300 cursor-pointer"
14-
>
15-
<div class="h-48 overflow-hidden relative">
16-
<img
17-
src={event.image}
18-
alt={event.title}
19-
class="w-full h-full object-cover transform hover:scale-105 transition-transform duration-300"
20-
/>
21-
<div class="absolute top-4 right-4">
22-
<span
23-
class={`px-3 py-1 rounded-full text-sm font-semibold ${
24-
event.modality === "Hybrid"
25-
? "bg-purple-100 text-purple-800"
26-
: event.modality === "Virtual"
27-
? "bg-green-100 text-green-800"
28-
: "bg-blue-100 text-blue-800"
29-
}`}
30-
>
31-
{event.modality}
32-
</span>
33-
</div>
34-
</div>
35-
<div class="p-6">
36-
<div class="flex justify-between items-start mb-3">
37-
<h2 class="text-xl font-bold text-gray-800">{event.title}</h2>
38-
</div>
39-
<p class="text-gray-600 mb-4 line-clamp-2">{event.description}</p>
40-
<div class="space-y-2 text-sm text-gray-500">
41-
<div class="flex items-center gap-2">
42-
<svg
43-
class="w-4 h-4"
44-
fill="none"
45-
stroke="currentColor"
46-
viewBox="0 0 24 24"
47-
>
48-
<path
49-
stroke-linecap="round"
50-
stroke-linejoin="round"
51-
stroke-width="2"
52-
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
53-
/>
54-
</svg>
55-
<span>
56-
{new Date(event.date).toLocaleDateString("en-US", {
57-
weekday: "long",
58-
year: "numeric",
59-
month: "long",
60-
day: "numeric",
61-
})}
62-
</span>
63-
</div>
64-
<div class="flex items-center gap-2">
65-
<svg
66-
class="w-4 h-4"
67-
fill="none"
68-
stroke="currentColor"
69-
viewBox="0 0 24 24"
70-
>
71-
<path
72-
stroke-linecap="round"
73-
stroke-linejoin="round"
74-
stroke-width="2"
75-
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
76-
/>
77-
<path
78-
stroke-linecap="round"
79-
stroke-linejoin="round"
80-
stroke-width="2"
81-
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
82-
/>
83-
</svg>
84-
<span>{event.place}</span>
85-
</div>
86-
<div class="flex items-center gap-2">
87-
<svg
88-
class="w-4 h-4"
89-
fill="none"
90-
stroke="currentColor"
91-
viewBox="0 0 24 24"
92-
>
93-
<path
94-
stroke-linecap="round"
95-
stroke-linejoin="round"
96-
stroke-width="2"
97-
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
98-
/>
99-
</svg>
100-
<span>{event.attendees} attendees</span>
101-
</div>
102-
</div>
103-
</div>
104-
</div>
13+
events.map((event:Event, index) => (
14+
<CardEvent {...event}/>
10515
))
10616
}
10717
</div>

0 commit comments

Comments
 (0)