Skip to content

Commit a08f5c1

Browse files
committed
feat: enhance TalkCard with type icons, colored borders, multiple speakers, LinkedIn integration and full descriptions
1 parent 683718f commit a08f5c1

1 file changed

Lines changed: 171 additions & 29 deletions

File tree

src/components/TalkCard.js

Lines changed: 171 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,180 @@
11
import Image from "next/image";
2+
import {
3+
LuCoffee, LuPartyPopper, LuHandshake, LuMic,
4+
LuWrench, LuMapPin, LuLinkedin
5+
} from "react-icons/lu";
26

3-
export default function TalkCard({ talk }) {
7+
export default function TalkCard({ talk, showRoom = false }) {
8+
const { title, time, speaker, speakers, description, tags, type, room } = talk;
9+
10+
// Normalizar speakers a un array
11+
const normalizedSpeakers = speakers
12+
? speakers
13+
: speaker
14+
? [speaker]
15+
: [];
16+
17+
// Mapa de estilos para todos los tipos de eventos
18+
const typeStyles = {
19+
recepcion: {
20+
border: "border-l-green-400",
21+
badge: {
22+
label: "Recepción",
23+
color: "bg-green-600/30 text-green-100",
24+
icon: <LuHandshake size={16} className="mr-1" />,
25+
}
26+
},
27+
break: {
28+
border: "border-l-yellow-400",
29+
badge: {
30+
label: "Break",
31+
color: "bg-yellow-500/30 text-yellow-100",
32+
icon: <LuCoffee size={16} className="mr-1" />,
33+
}
34+
},
35+
cierre: {
36+
border: "border-l-red-400",
37+
badge: {
38+
label: "Cierre",
39+
color: "bg-red-600/30 text-red-100",
40+
icon: <LuPartyPopper size={16} className="mr-1" />,
41+
}
42+
},
43+
charla: {
44+
border: "border-l-blue-400",
45+
badge: {
46+
label: "Charla",
47+
color: "bg-blue-500/30 text-blue-100",
48+
icon: <LuMic size={16} className="mr-1" />,
49+
}
50+
},
51+
taller: {
52+
border: "border-l-orange-400",
53+
badge: {
54+
label: "Taller",
55+
color: "bg-orange-500/30 text-orange-100",
56+
icon: <LuWrench size={16} className="mr-1" />,
57+
}
58+
},
59+
};
60+
61+
const isGeneral = !normalizedSpeakers.length &&
62+
(type === 'recepcion' || type === 'break' || type === 'cierre');
63+
64+
const style = typeStyles[type] || typeStyles.charla; // Default a charla si no se reconoce
65+
466
return (
5-
<div className="bg-black/20 backdrop-blur-sm rounded-lg overflow-hidden shadow-lg hover:shadow-2xl transition-all duration-300 hover:-translate-y-1">
6-
<div className="p-6">
7-
<div className="flex items-center space-x-4 mb-4">
8-
<div className="relative w-12 h-12 rounded-full overflow-hidden bg-purple-700">
9-
{talk.speaker.image && (
10-
<Image
11-
src={talk.speaker.image}
12-
alt={talk.speaker.name}
13-
fill
14-
className="object-cover"
15-
/>
67+
<div
68+
className={`bg-black/20 backdrop-blur-sm rounded-lg overflow-hidden shadow-lg hover:shadow-2xl transition-all duration-300 hover:-translate-y-1
69+
${style.border} border-l-4 h-full flex flex-col`}
70+
>
71+
<div className="p-4 md:p-6 flex flex-col flex-grow">
72+
{isGeneral ? (
73+
<div className="flex flex-col md:flex-row md:items-center justify-between mb-3">
74+
<div className="mb-2 md:mb-0">
75+
<p className="text-xs md:text-sm font-medium opacity-80">{time}</p>
76+
<h3 className="font-bold text-base md:text-lg">{title}</h3>
77+
{showRoom && room && (
78+
<div className="flex items-center mt-1 text-xs opacity-70">
79+
<LuMapPin size={12} className="mr-1" />
80+
<span>{room}</span>
81+
</div>
82+
)}
83+
</div>
84+
<div
85+
className={`px-2 py-1 rounded-full text-xs md:text-sm flex items-center font-semibold ${style.badge.color} self-start md:self-auto`}
86+
>
87+
{style.badge.icon}
88+
{style.badge.label}
89+
</div>
90+
</div>
91+
) : (
92+
<div className="mb-4">
93+
{/* Fila de tiempo y badge */}
94+
<div className="flex justify-between items-start mb-2">
95+
<p className="text-xs md:text-sm font-medium opacity-80">{time}</p>
96+
<div
97+
className={`px-2 py-1 rounded-full text-xs flex items-center font-semibold ${style.badge.color}`}
98+
>
99+
{style.badge.icon}
100+
{style.badge.label}
101+
</div>
102+
</div>
103+
104+
{/* Título */}
105+
<h3 className="font-bold text-base md:text-lg mb-2">{title}</h3>
106+
107+
{/* Speakers con imágenes */}
108+
{normalizedSpeakers.length > 0 && (
109+
<div className="flex flex-wrap items-center gap-3 mb-3">
110+
{normalizedSpeakers.map((spkr, idx) => (
111+
<div key={idx} className="flex items-center">
112+
{/* Imagen optimizada para webp 320x320 */}
113+
<div className="relative w-14 h-14 md:w-16 md:h-16 rounded-full overflow-hidden bg-purple-700 flex-shrink-0">
114+
{spkr.image && (
115+
<Image
116+
src={spkr.image}
117+
alt={spkr.name}
118+
fill
119+
className="object-cover"
120+
sizes="(max-width: 640px) 56px, 64px"
121+
quality={85}
122+
/>
123+
)}
124+
</div>
125+
<div className="ml-2 min-w-0">
126+
<p className="text-xs md:text-sm font-medium truncate">{spkr.name}</p>
127+
128+
{/* Solo LinkedIn */}
129+
{spkr.socials?.linkedin && (
130+
<a
131+
href={spkr.socials.linkedin}
132+
target="_blank"
133+
rel="noopener noreferrer"
134+
className="flex items-center mt-1 text-xs opacity-70 hover:opacity-100 transition-opacity"
135+
title="Perfil de LinkedIn"
136+
>
137+
<LuLinkedin size={12} className="mr-1 flex-shrink-0" />
138+
<span className="hidden sm:inline truncate">LinkedIn</span>
139+
</a>
140+
)}
141+
</div>
142+
</div>
143+
))}
144+
</div>
145+
)}
146+
147+
{/* Sala */}
148+
{showRoom && room && (
149+
<div className="flex items-center mb-2 text-xs opacity-70">
150+
<LuMapPin size={12} className="mr-1" />
151+
<span>{room}</span>
152+
</div>
16153
)}
17154
</div>
18-
<div>
19-
<p className="text-sm font-medium opacity-80">{talk.time}</p>
20-
<h3 className="font-bold text-lg">{talk.title}</h3>
21-
<p className="text-sm">{talk.speaker.name}</p>
155+
)}
156+
157+
{/* Descripción */}
158+
{description && (
159+
<p className="text-xs md:text-sm opacity-80 mb-3 flex-grow">
160+
{description}
161+
</p>
162+
)}
163+
164+
{/* Tags */}
165+
{tags && tags.length > 0 && (
166+
<div className="mt-auto flex flex-wrap gap-1 md:gap-2">
167+
{tags.map((tag, index) => (
168+
<span
169+
key={index}
170+
className="text-xs bg-green-600/40 hover:bg-green-600/60 px-2 py-1 rounded-full transition-colors cursor-pointer whitespace-nowrap"
171+
>
172+
{tag}
173+
</span>
174+
))}
22175
</div>
23-
</div>
24-
<p className="text-sm opacity-80 line-clamp-2">{talk.description}</p>
25-
<div className="mt-4 flex flex-wrap gap-2">
26-
{talk.tags.map((tag, index) => (
27-
<span
28-
key={index}
29-
className="text-xs bg-green-600/40 hover:bg-green-600/60 px-2 py-1 rounded-full transition-colors cursor-pointer"
30-
>
31-
{tag}
32-
</span>
33-
))}
34-
</div>
176+
)}
35177
</div>
36178
</div>
37179
);
38-
}
180+
}

0 commit comments

Comments
 (0)