Calendar & Assistants
CI/CD Deploy / test (push) Successful in 1m2s
CI/CD Deploy / build-push (push) Successful in 1m34s
CI/CD Deploy / deploy (push) Successful in 15s

This commit is contained in:
Antoni Nuñez Romeu
2026-08-25 14:23:05 +02:00
parent 6f2027610f
commit c799cdda00
5 changed files with 91 additions and 74 deletions
+40 -57
View File
@@ -1,8 +1,6 @@
import { motion } from "framer-motion";
import { useState, useEffect } from "react";
import { SCHEDULE, EVENT_INFO } from "@/data/event-data";
import { SCHEDULE } from "@/data/event-data";
import { Clock, Music, Coffee, Star } from "lucide-react";
import { getTimeLeft } from "@/components/HeroSection";
import { useTranslation } from "react-i18next";
const typeIcon: Record<string, typeof Clock> = {
@@ -19,60 +17,22 @@ const typeColor: Record<string, string> = {
show: "border-accent bg-accent/10 text-accent",
};
type ScheduleEvent = {
time: string;
title: string;
type: string;
parallel?: ScheduleEvent[];
};
const ScheduleSection = () => {
const { t } = useTranslation();
const [timeLeft, setTimeLeft] = useState(getTimeLeft(EVENT_INFO.date));
const isEventOngoing = timeLeft.days === 0 && timeLeft.hours === 0 && timeLeft.minutes === 0 && timeLeft.seconds === 0;
const dayKeys = ["friday", "saturday", "sunday"] as const;
const formatEventTitle = (event: { type: string; title: string }, dayIndex: number) => {
switch (event.type) {
case "workshop": {
const match = event.title.match(/Workshop\\s*(\\d+)/i);
return match?.[1] ? `${t("schedule.workshop")} ${match[1]}` : t("schedule.workshop");
}
case "break":
return t("schedule.break");
case "show":
return t("schedule.show");
case "social":
return dayIndex === 2 ? t("schedule.farewell") : t("schedule.social");
default:
return event.title;
}
return event.title;
};
useEffect(() => {
const timer = setInterval(() => {
setTimeLeft(getTimeLeft(EVENT_INFO.date));
}, 1000);
return () => clearInterval(timer);
}, []);
if (!isEventOngoing) {
return (
<section
id="schedule"
className="section-padding bg-card relative z-20 -mt-[40px] pt-[100px]"
style={{ borderRadius: "100% 0 0 0 / 120px 0 0 0" }}
>
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
className="max-w-lg mx-auto text-center bg-card rounded-2xl p-10 shadow-elevated"
>
<h3 className="font-display text-2xl md:text-3xl lg:text-4xl pt-3 pb-5 leading-[1.6] font-bold text-foreground mb-2">
{t("schedule.statusBriefTitle")}
</h3>
<p className="text-muted-foreground">
{t("schedule.statusBriefDescription")}
</p>
</motion.div>
</section>
);
}
return (
<section
id="schedule"
@@ -108,21 +68,44 @@ const ScheduleSection = () => {
</h3>
<div className="space-y-3">
{day.events.map((event, ei) => {
if (event.parallel) {
return (
<div key={ei} className="flex items-center gap-3 p-3 rounded-lg border-l-4 border-primary bg-primary/5">
<p className="text-xs font-medium opacity-70 whitespace-nowrap">{event.time}</p>
<div className="flex items-center gap-2 flex-1">
{event.parallel.map((pEvent, pi) => {
const PIcon = typeIcon[pEvent.type] || Clock;
return (
<div
key={pi}
className={`flex items-center gap-3 p-3 rounded-lg border-l-4 flex-1 ${
typeColor[pEvent.type] || ""
}`}
>
<PIcon className="w-4 h-4 shrink-0" />
<p className="text-sm font-semibold">
{pEvent.title}
</p>
</div>
);
})}
</div>
</div>
);
}
const Icon = typeIcon[event.type] || Clock;
return (
<div
key={ei}
className={`flex items-start gap-3 p-3 rounded-lg border-l-4 ${
className={`flex items-center gap-3 p-3 rounded-lg border-l-4 ${
typeColor[event.type] || ""
}`}
>
<Icon className="w-4 h-4 mt-0.5 shrink-0" />
<div>
<p className="text-xs font-medium opacity-70">{event.time}</p>
<p className="text-sm font-semibold">
{formatEventTitle(event as { type: string; title: string }, di)}
</p>
</div>
<Icon className="w-4 h-4 shrink-0" />
<p className="text-xs font-medium opacity-70 whitespace-nowrap">{event.time}</p>
<p className="text-sm font-semibold">
{formatEventTitle(event as { type: string; title: string }, di)}
</p>
</div>
);
})}