diff --git a/relume-test/src/components/Event33.tsx b/relume-test/src/components/Event33.tsx index 8b0e700..384687b 100644 --- a/relume-test/src/components/Event33.tsx +++ b/relume-test/src/components/Event33.tsx @@ -1,283 +1,79 @@ -import { Button, Tabs, TabsContent, TabsList, TabsTrigger } from "@relume_io/relume-ui"; -import type { ButtonProps } from "@relume_io/relume-ui"; -import { BiMap } from "react-icons/bi"; -type ImageProps = { - src: string; - alt?: string; -}; +export type EventLevel = "Senior" | "Mid-Level" | "Junior"; +export type EventType = "Talk" | "Workshop" | "Interaction" | "AI and Future"; +export type EventDay = "Donnerstag" | "Freitag"; -type ScheduledEvent = { - url: string; +export type ProgramEvent = { + id: string; time: string; - image: ImageProps; + image: { src: string; alt: string }; title: string; speaker: string; - location: string; - button: ButtonProps; -}; - -type Tab = { - value: string; - trigger: string; - content: ScheduledEvent[]; + type: EventType; + level: EventLevel; + day: EventDay; + speakerUrl: string; }; type Props = { - tagline: string; - heading: string; - description: string; - tabs: Tab[]; + events: ProgramEvent[]; }; -export type Event33Props = React.ComponentPropsWithoutRef<"section"> & Partial; +const levelStyles: Record = { + Senior: "bg-electric-violet text-cloud-white", + "Mid-Level": "bg-electric-violet text-cloud-white", + Junior: "bg-acid-lime text-tech-navy", +}; -export const Event33 = (props: Event33Props) => { - const { tagline, heading, description, tabs } = { - ...Event33Defaults, - ...props, - }; +const EventRow = ({ event }: { event: ProgramEvent }) => ( +
+ {event.time} +
+ {event.image.alt} +
+ +
+
{event.title}
+

{event.speaker}

+
+ + + {event.level} + + + + Über {event.speaker.split(" ")[0]} {event.speaker.split(" ").slice(-1)[0]} + + + + Tickets kaufen + +
+); + +export const Event33 = ({ events }: Props) => { return ( -
+
-
-
-

{tagline}

-

{heading}

-

{description}

-
-
- - - {tabs.map((tab, index) => ( - - {tab.trigger} - + {events.length === 0 ? ( +

Keine Veranstaltungen gefunden.

+ ) : ( +
+ {events.map((event) => ( + ))} - - {tabs.map((tab) => ( - - {tab.content.map((event, index) => ( - - ))} - - ))} - +
+ )}
); }; -const ScheduledEvent: React.FC = ({ - url, - time, - image, - title, - speaker, - location, - button, -}) => { - return ( -
-
{time}
-
- - {image.alt} - -
-
-
-
{title}
-
{speaker}
-
-
- - {location} -
-
- -
- ); -}; - -export const Event33Defaults: Props = { - tagline: "Tagline", - heading: "Schedule", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique.", - tabs: [ - { - value: "fri-09-feb", - trigger: "Fri 09 Feb", - content: [ - { - url: "#", - time: "8:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 1", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - { - url: "#", - time: "9:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 2", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - { - url: "#", - time: "10:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 3", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - ], - }, - { - value: "sat-10-feb", - trigger: "Sat 10 Feb", - content: [ - { - url: "#", - time: "8:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 4", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - { - url: "#", - time: "9:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 5", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - { - url: "#", - time: "10:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 6", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - ], - }, - { - value: "sun-11-feb", - trigger: "Sun 11 Feb", - content: [ - { - url: "#", - time: "8:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 7", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - { - url: "#", - time: "9:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 8", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - { - url: "#", - time: "10:00 am", - image: { - src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", - alt: "Relume placeholder image 9", - }, - title: "Event title heading", - speaker: "Speaker", - location: "Location", - button: { - variant: "secondary", - size: "primary", - title: "View details", - }, - }, - ], - }, - ], -}; +export default Event33; diff --git a/relume-test/src/components/Filters5.tsx b/relume-test/src/components/Filters5.tsx index 848097f..12360f1 100644 --- a/relume-test/src/components/Filters5.tsx +++ b/relume-test/src/components/Filters5.tsx @@ -1,316 +1,240 @@ -import React, { useState } from "react"; +import { useState } from "react"; import { BiFilter, BiSearch } from "react-icons/bi"; -import { RxChevronDown } from "react-icons/rx"; import { IoCloseOutline } from "react-icons/io5"; +import { RxChevronDown } from "react-icons/rx"; import { AnimatePresence, motion } from "framer-motion"; -import type { Variants } from "framer-motion"; -import { - Button, - Checkbox, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, - Input, - Label, - Select, - SelectContent, - SelectTrigger, - SelectValue, - Tabs, - TabsList, - TabsTrigger, -} from "@relume_io/relume-ui"; -type FilterValues = { - filterOne: string; - filterTwo: string[]; - filterThree: string[]; - filterFour: string[]; +export type FilterTab = { value: string; label: string }; +type ActiveTag = { label: string; onRemove: () => void }; + +type Props = { + tabs?: FilterTab[]; + activeTab?: string; + onTabChange?: (tab: string) => void; + searchQuery?: string; + onSearchChange?: (q: string) => void; + searchLabel?: string; + resetLabel?: string; + activeTags?: ActiveTag[]; + onReset?: () => void; + // Programm-only + useFilterToggle?: boolean; + showDayFilter?: boolean; + showSortButton?: boolean; + activeDay?: string; + onDayChange?: (day: string) => void; }; -const tabs = [ - { value: "View all", trigger: "View all" }, - { value: "Category one", trigger: "Category one" }, - { value: "Category two", trigger: "Category two" }, - { value: "Category three", trigger: "Category three" }, - { value: "Category four", trigger: "Category four" }, +const PROGRAMM_TABS: FilterTab[] = [ + { value: "all", label: "Alle Anzeigen" }, + { value: "Talk", label: "Talk" }, + { value: "Workshop", label: "Workshop" }, + { value: "Interaction", label: "Interaction" }, + { value: "AI and Future", label: "AI and Future" }, + { value: "Senior", label: "Senior Level" }, + { value: "Mid-Level", label: "Mid-Level" }, + { value: "Junior", label: "Junior-Level" }, ]; -const options = [ - { value: "option-1", label: "Option one" }, - { value: "option-2", label: "Option two" }, - { value: "option-3", label: "Option three" }, - { value: "option-4", label: "Option four" }, - { value: "option-5", label: "Option five" }, +const DAYS = [ + { value: "all", label: "Alle Tage" }, + { value: "Donnerstag", label: "Donnerstag" }, + { value: "Freitag", label: "Freitag" }, ]; -const containerVariants: Variants = { - initial: { - height: 0, - opacity: 0, - transition: { duration: 0.2, ease: "easeInOut" }, - }, - animate: { - height: "auto", - opacity: 1, - transition: { duration: 0.2, ease: "easeInOut" }, - }, - exit: { - height: 0, - opacity: 0, - transition: { duration: 0.2, ease: "easeInOut" }, - }, -}; - -export const Filters5 = () => { +export const Filters5 = ({ + tabs = PROGRAMM_TABS, + activeTab = "all", + onTabChange = () => {}, + searchQuery = "", + onSearchChange = () => {}, + searchLabel = "Stichwortsuche", + resetLabel = "Zurücksetzen", + activeTags = [], + onReset = () => {}, + useFilterToggle = true, + showDayFilter = true, + showSortButton = true, + activeDay = "all", + onDayChange = () => {}, +}: Props) => { const [isOpen, setIsOpen] = useState(false); - const [selectedValues, setSelectedValues] = useState({ - filterOne: "", - filterTwo: ["option-1"], - filterThree: ["option-1"], - filterFour: ["option-1"], - }); + const [sortOpen, setSortOpen] = useState(false); - const handleClear = (filter: keyof FilterValues) => { - setSelectedValues((prev) => ({ - ...prev, - [filter]: Array.isArray(prev[filter]) ? [] : "", - })); - }; - - const handleCheckboxChange = (filter: keyof Omit, value: string) => { - setSelectedValues((prev) => { - const currentItems = prev[filter]; - return { - ...prev, - [filter]: currentItems.includes(value) - ? currentItems.filter((item) => item !== value) - : [...currentItems, value], - }; - }); - }; - - const getSelectedLabels = (filter: keyof FilterValues) => { - if (!selectedValues[filter].length) return "Select options"; - if (selectedValues[filter].length === 1) { - return options.find((opt) => opt.value === selectedValues[filter][0])?.label; - } - return `${selectedValues[filter].length} selected`; - }; - - return ( -
-
-
-

Collection

-

Lorem ipsum dolor sit amet.

-
-
- - - - {tabs.map((tab, index) => ( - + ))} +
+ ); + + const SearchRow = () => ( +
+
+ {searchLabel} + +
+
+ + onSearchChange(e.target.value)} + /> +
+
+ ); + + // Speaker variant: always-visible search, no toggle + if (!useFilterToggle) { + return ( +
+
+ + + {activeTags.length > 0 && ( +
+ {activeTags.map((tag, i) => ( + - {tab.trigger} - + {tag.label} + + ))} - - - - -

Sort by

- -
- - {[ - "Most Popular", - "Most Recent", - "Name: A to Z", - "Name: Z to A", - "Price: Low to High", - "Price: High to Low", - ].map((option) => ( - {option} - ))} - -
+
+ )}
- +
+ ); + } + + // Programm variant: toggle button, collapsible panel + return ( +
+
+
+ + + {showSortButton && ( +
+ + {sortOpen && ( +
+ {["Zeit aufsteigend", "Zeit absteigend"].map((opt) => ( + + ))} +
+ )} +
+ )} +
+ + {isOpen && ( -
-
-
- Filter One - -
- } - value={selectedValues.filterOne} - onChange={(e) => - setSelectedValues((prev) => ({ - ...prev, - filterOne: e.target.value, - })) - } - className="border-electric-violet rounded-2xl" - /> +
+
+ + onSearchChange(e.target.value)} + />
-
-
- Filter Two - + {showDayFilter && ( +
+ + Filtern nach Eventtage + + {}}> - - - - - {options.map((option) => ( -
- handleCheckboxChange("filterTwo", option.value)} - /> - -
+ {DAYS.map((d) => ( + ))} -
- -
-
-
- Filter Three - +
- -
-
-
- Filter Four - -
- -
+ )}
+ {activeTags.length > 0 && ( +
+ {activeTags.map((tag, i) => ( + + {tag.label} + + + ))} +
+ )} )} -
-
-
); -}; \ No newline at end of file +}; + +export default Filters5; diff --git a/relume-test/src/components/Team4.tsx b/relume-test/src/components/Team4.tsx index a73bd4a..278bb7f 100644 --- a/relume-test/src/components/Team4.tsx +++ b/relume-test/src/components/Team4.tsx @@ -2,170 +2,199 @@ import speaker1 from "../assets/speakers/speaker1.jpg"; import speaker2 from "../assets/speakers/speaker2.jpg"; import speaker3 from "../assets/speakers/speaker3.jpg"; import speaker4 from "../assets/speakers/speaker4.jpg"; -import { Button } from "@relume_io/relume-ui"; -import type { ButtonProps } from "@relume_io/relume-ui"; -import { BiLogoDribbble, BiLogoLinkedinSquare } from "react-icons/bi"; -import { FaXTwitter } from "react-icons/fa6"; +import { BiLogoLinkedinSquare } from "react-icons/bi"; type ImageProps = { src: string; alt?: string; }; -type Footer = { - heading: string; - description: string; - button: ButtonProps; -}; - type SocialLink = { href: string; icon: React.ReactNode; }; -type TeamMember = { +type Talk = { + title: string; + type: "Talk" | "Workshop" | "Networking" | "AI and Future" | "Interaction"; +}; + +export type TeamMember = { image: ImageProps; name: string; - jobTitle: string; + role: string; + company: string; + companyUrl?: string; description: string; + category: string; + talks: Talk[]; socialLinks: SocialLink[]; }; type Props = { - tagline: string; - heading: string; - description: string; teamMembers: TeamMember[]; - footerContent: Footer; }; export type Team4Props = React.ComponentPropsWithoutRef<"section"> & Partial; +const talkBadgeClass = (type: Talk["type"]) => { + if (type === "Workshop") return "bg-acid-lime text-tech-navy"; + if (type === "Networking") return "bg-acid-lime text-tech-navy"; + return "bg-electric-violet text-cloud-white"; +}; + +const MemberCard = ({ member }: { member: TeamMember }) => ( +
+
+ {member.image.alt} +
+
{member.name}
+

{member.role}

+ + {member.company} + +

{member.description}

+
+ {member.talks.map((talk, i) => ( + + {talk.title} + + ))} +
+
+ {member.socialLinks.map((link, i) => ( + + {link.icon} + + ))} +
+
+); + export const Team4 = (props: Team4Props) => { - const { tagline, heading, description, teamMembers, footerContent } = { - ...Team4Defaults, - ...props, - }; + const { teamMembers } = { ...Team4Defaults, ...props }; + return ( -
+
-
-

{tagline}

-

- {heading} -

-

{description}

-
-
- {teamMembers.map((member, index) => ( - - ))} -
-
-

- {footerContent.heading} -

-

{footerContent.description}

-
- + {teamMembers.length === 0 ? ( +

Keine Speaker gefunden.

+ ) : ( +
+ {teamMembers.map((member, i) => ( + + ))}
-
+ )}
); }; -const TeamMember = ({ member }: { member: TeamMember }) => { - return ( -
-
- {member.image.alt} -
-
-
{member.name}
-
{member.jobTitle}
-
-

{member.description}

-
- {member.socialLinks.map((link, index) => ( - - {link.icon} - - ))} -
-
- ); -}; - export const Team4Defaults: Props = { - tagline: "", - heading: "Unsere Speaker", - description: "", teamMembers: [ { image: { src: speaker1, alt: "Jens Riegelsberger" }, name: "Jens Riegelsberger", - jobTitle: "UX Director · Google", - description: "Jens leitet die UX-Teams für Search und Maps sowie die globale UXR-Infrastruktur.", + role: "UX Director", + company: "Google", + description: + "Jens leitet die UX-Teams für Search und Maps sowie die globale UXR-Infrastruktur. Der HCI-Experte lehrte als Gastprofessor an der UdK Berlin und bringt Erfahrung von Stationen bei Microsoft Research, Amazon und Apple mit.", + category: "Research", + talks: [ + { title: "Talk: Scaling Research", type: "Talk" }, + { title: "Workshop: Skalierbare Research-Infrastruktur", type: "Workshop" }, + ], socialLinks: [{ href: "#", icon: }], }, { image: { src: speaker2, alt: "Marcus J. Low" }, name: "Marcus J. Low", - jobTitle: "Principal Designer · Airbnb", - description: "Als Mitentwickler des ersten Airbnb Design Systems spricht Marcus über die Balance zwischen Markenästhetik und funktionaler Logik.", + role: "Principal Designer", + company: "Airbnb", + description: + "Als Mitentwickler des ersten Airbnb Design Systems spricht Marcus über die Balance zwischen Markenästhetik und funktionaler Logik.", + category: "Design / UX", + talks: [{ title: "Workshop: The Future of Tokens", type: "Workshop" }], socialLinks: [{ href: "#", icon: }], }, { image: { src: speaker3, alt: "Reto Gwerder" }, name: "Reto Gwerder", - jobTitle: "Head of Product · Ginetta", - description: "Reto steht für Schweizer Design-Qualität. Er analysiert, warum Simplizität oft die größte technische Herausforderung ist.", + role: "Head of Product", + company: "Ginetta", + description: + "Reto steht für Schweizer Design-Qualität. Er analysiert, warum Simplizität oft die größte technische Herausforderung ist.", + category: "Strategie", + talks: [{ title: "Talk: Simplify or Die", type: "Talk" }], socialLinks: [{ href: "#", icon: }], }, { image: { src: speaker4, alt: "Dr. Elena Rossi" }, name: "Dr. Elena Rossi", - jobTitle: "Cognitive Psychologist · University of Milan", - description: "Elena verbindet Wissenschaft mit Design. Sie erklärt, wie unser Gehirn auf Micro-Interactions reagiert.", + role: "Cognitive Psychologist", + company: "University of Milan", + description: + "Elena verbindet Wissenschaft mit Design. Sie erklärt, wie unser Gehirn auf Micro-Interactions reagiert und wann wir uns manipuliert fühlen.", + category: "Psychologie", + talks: [{ title: "Deep Dive: Psychological UX", type: "Talk" }], socialLinks: [{ href: "#", icon: }], }, { - image: { src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", alt: "Dr. Elena Rossi" }, - name: "Dr. Elena Rossi", - jobTitle: "Cognitive Psychologist · University of Milan", - description: "Elena verbindet Wissenschaft mit Design. Sie erklärt, wie unser Gehirn auf Micro-Interactions reagiert.", + image: { src: speaker1, alt: "Liam O'Connor" }, + name: "Liam O'Connor", + role: "Lead Product Designer", + company: "Spotify", + description: + "Liam gibt Einblicke, wie Spotify Personalisierung nutzt, ohne die Privatsphäre der Nutzer zu verletzen.", + category: "AI & Future", + talks: [{ title: "Talk: Trust in AI UX", type: "Talk" }], socialLinks: [{ href: "#", icon: }], }, { - image: { src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", alt: "Dr. Elena Rossi" }, - name: "Dr. Elena Rossi", - jobTitle: "Cognitive Psychologist · University of Milan", - description: "Elena verbindet Wissenschaft mit Design. Sie erklärt, wie unser Gehirn auf Micro-Interactions reagiert.", + image: { src: speaker2, alt: "Fabienne Keller" }, + name: "Fabienne Keller", + role: "Gründerin", + company: "User-First Agency", + description: + "Fabienne ist Expertin für User Research in komplexen B2B-Umfeldern und wie man Stakeholder von Test-Ergebnissen überzeugt.", + category: "Research", + talks: [{ title: "Workshop: Research Repositories", type: "Workshop" }], socialLinks: [{ href: "#", icon: }], }, { - image: { src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", alt: "Dr. Elena Rossi" }, - name: "Dr. Elena Rossi", - jobTitle: "Cognitive Psychologist · University of Milan", - description: "Elena verbindet Wissenschaft mit Design. Sie erklärt, wie unser Gehirn auf Micro-Interactions reagiert.", + image: { src: speaker3, alt: "Thomas Meyer" }, + name: 'Thomas "Tom" Meyer', + role: "Creative Director", + company: "Swisscom", + description: + "Tom zeigt, wie man in großen Konzernen eine Design-Kultur etabliert, die über das visuelle Layer hinausgeht.", + category: "Strategie", + talks: [{ title: "Networking: Leading Teams", type: "Networking" }], socialLinks: [{ href: "#", icon: }], }, { - image: { src: "https://d22po4pjz3o32e.cloudfront.net/placeholder-image.svg", alt: "Dr. Elena Rossi" }, - name: "Dr. Elena Rossi", - jobTitle: "Cognitive Psychologist · University of Milan", - description: "Elena verbindet Wissenschaft mit Design. Sie erklärt, wie unser Gehirn auf Micro-Interactions reagiert.", + image: { src: speaker4, alt: "Sarah M. Widmer" }, + name: "Sarah M. Widmer", + role: "Senior UX Architect", + company: "SBB", + description: + "Sarah gestaltet die Mobilität von morgen. Sie zeigt, wie man Millionen von Nutzern barrierefrei durch den digitalen öV-Dschungel leitet.", + category: "Design / UX", + talks: [{ title: "Talk: Inclusive Design at Scale", type: "Talk" }], socialLinks: [{ href: "#", icon: }], }, ], - footerContent: { - heading: "", - description: "", - button: { title: "", variant: "secondary" }, - }, }; + +export default Team4; diff --git a/relume-test/src/pages/Programm.tsx b/relume-test/src/pages/Programm.tsx index 68333aa..8310fac 100644 --- a/relume-test/src/pages/Programm.tsx +++ b/relume-test/src/pages/Programm.tsx @@ -1,21 +1,92 @@ +import { useMemo, useState } from "react"; import { Navbar3 } from "../components/Navbar"; import { Header23 } from "../components/Header23"; import { Filters5 } from "../components/Filters5"; import { Event33 } from "../components/Event33"; +import type { ProgramEvent } from "../components/Event33"; import { Banner15 } from "../components/Banner15"; import { Footer3 } from "../components/Footer"; +import spk1 from "../assets/speakers/speaker1.jpg"; +import spk2 from "../assets/speakers/speaker2.jpg"; +import spk3 from "../assets/speakers/speaker3.jpg"; +import spk4 from "../assets/speakers/speaker4.jpg"; + +const ALL_EVENTS: ProgramEvent[] = [ + // Donnerstag + { id: "do-1", time: "10:00 – 11:30 Uhr", image: { src: spk1, alt: "Jens Riegelsberger" }, title: "Opening Keynote: The Future of UX", speaker: "Jens Riegelsberger", type: "Talk", level: "Senior", day: "Donnerstag", speakerUrl: "/speaker" }, + { id: "do-2", time: "12:00 – 13:00 Uhr", image: { src: spk2, alt: "Marcus J. Low" }, title: "Workshop: Design Systems at Scale", speaker: "Marcus J. Low", type: "Workshop", level: "Mid-Level", day: "Donnerstag", speakerUrl: "/speaker" }, + { id: "do-3", time: "14:00 – 15:30 Uhr", image: { src: spk3, alt: "Reto Gwerder" }, title: "Interaction: Prototyping Fast", speaker: "Reto Gwerder", type: "Interaction", level: "Junior", day: "Donnerstag", speakerUrl: "/speaker" }, + { id: "do-4", time: "16:00 – 17:30 Uhr", image: { src: spk4, alt: "Dr. Elena Rossi" }, title: "AI and Future: Intelligence in Design", speaker: "Dr. Elena Rossi", type: "AI and Future", level: "Senior", day: "Donnerstag", speakerUrl: "/speaker" }, + { id: "do-5", time: "18:00 – 19:30 Uhr", image: { src: spk1, alt: "Liam O'Connor" }, title: "Talk: Ethical AI", speaker: "Liam O'Connor", type: "Talk", level: "Mid-Level", day: "Donnerstag", speakerUrl: "/speaker" }, + // Freitag + { id: "fr-1", time: "12:00 – 13:30 Uhr", image: { src: spk1, alt: "Jens Riegelsberger" }, title: "Scaling Research", speaker: "Jens Riegelsberger", type: "Talk", level: "Senior", day: "Freitag", speakerUrl: "/speaker" }, + { id: "fr-2", time: "12:00 – 13:00 Uhr", image: { src: spk2, alt: "Marcus J. Low" }, title: "Workshop: The Future of Tokens", speaker: "Marcus J. Low", type: "Workshop", level: "Mid-Level", day: "Freitag", speakerUrl: "/speaker" }, + { id: "fr-3", time: "14:00 – 15:30 Uhr", image: { src: spk3, alt: "Reto Gwerder" }, title: "Talk: Simplify or Die", speaker: "Reto Gwerder", type: "Talk", level: "Junior", day: "Freitag", speakerUrl: "/speaker" }, + { id: "fr-4", time: "16:00 – 17:30 Uhr", image: { src: spk4, alt: "Dr. Elena Rossi" }, title: "Deep Dive: Psychological UX", speaker: "Dr. Elena Rossi", type: "Talk", level: "Senior", day: "Freitag", speakerUrl: "/speaker" }, + { id: "fr-5", time: "18:00 – 19:30 Uhr", image: { src: spk1, alt: "Liam O'Connor" }, title: "Talk: Trust in AI", speaker: "Liam O'Connor", type: "Talk", level: "Mid-Level", day: "Freitag", speakerUrl: "/speaker" }, + { id: "fr-6", time: "20:00 – 21:30 Uhr", image: { src: spk2, alt: "Fabienne Keller" }, title: "Workshop: Research Repositories", speaker: "Fabienne Keller", type: "Workshop", level: "Senior", day: "Freitag", speakerUrl: "/speaker" }, +]; + +const TYPE_TABS = ["Talk", "Workshop", "Interaction", "AI and Future"]; +const LEVEL_TABS: Record = { + "Senior": "Senior", + "Mid-Level": "Mid-Level", + "Junior": "Junior", +}; + const Programm = () => { + const [activeTab, setActiveTab] = useState("all"); + const [searchQuery, setSearchQuery] = useState(""); + const [activeDay, setActiveDay] = useState("all"); + + const filteredEvents = useMemo(() => { + return ALL_EVENTS.filter((e) => { + if (activeTab !== "all") { + const matchesType = TYPE_TABS.includes(activeTab) && e.type === activeTab; + const matchesLevel = LEVEL_TABS[activeTab] && e.level === LEVEL_TABS[activeTab]; + if (!matchesType && !matchesLevel) return false; + } + if (searchQuery) { + const q = searchQuery.toLowerCase(); + if (!e.title.toLowerCase().includes(q) && !e.speaker.toLowerCase().includes(q)) return false; + } + if (activeDay !== "all" && e.day !== activeDay) return false; + return true; + }); + }, [activeTab, searchQuery, activeDay]); + + const activeTags = [ + searchQuery ? { label: searchQuery, onRemove: () => setSearchQuery("") } : null, + activeDay !== "all" ? { label: activeDay, onRemove: () => setActiveDay("all") } : null, + activeTab !== "all" ? { label: activeTab, onRemove: () => setActiveTab("all") } : null, + ].filter(Boolean) as { label: string; onRemove: () => void }[]; + + const handleReset = () => { + setActiveTab("all"); + setSearchQuery(""); + setActiveDay("all"); + }; + return (
- - + +
); }; -export default Programm; \ No newline at end of file +export default Programm; diff --git a/relume-test/src/pages/Speaker.tsx b/relume-test/src/pages/Speaker.tsx index ef4bf00..1b78b33 100644 --- a/relume-test/src/pages/Speaker.tsx +++ b/relume-test/src/pages/Speaker.tsx @@ -1,26 +1,76 @@ +import { useMemo, useState } from "react"; import { Navbar3 } from "../components/Navbar"; import { Header23 } from "../components/Header23"; import { Filters5 } from "../components/Filters5"; import { Team4 } from "../components/Team4"; +import { Team4Defaults } from "../components/Team4"; import { Footer3 } from "../components/Footer"; -const Programm = () => { +const SPEAKER_TABS = [ + { value: "all", label: "Alle ansehen" }, + { value: "Strategie", label: "Strategie" }, + { value: "Research", label: "Research" }, + { value: "Design / UX", label: "Design / UX" }, + { value: "AI & Future", label: "AI & Future" }, + { value: "Psychologie", label: "Psychologie" }, +]; + +const ALL_SPEAKERS = Team4Defaults.teamMembers; + +const Speaker = () => { + const [activeTab, setActiveTab] = useState("all"); + const [searchQuery, setSearchQuery] = useState(""); + + const filteredSpeakers = useMemo(() => { + return ALL_SPEAKERS.filter((s) => { + if (activeTab !== "all" && s.category !== activeTab) return false; + if (searchQuery) { + const q = searchQuery.toLowerCase(); + if ( + !s.name.toLowerCase().includes(q) && + !s.role.toLowerCase().includes(q) && + !s.company.toLowerCase().includes(q) + ) + return false; + } + return true; + }); + }, [activeTab, searchQuery]); + + const activeTags = [ + searchQuery ? { label: searchQuery, onRemove: () => setSearchQuery("") } : null, + activeTab !== "all" ? { label: activeTab, onRemove: () => setActiveTab("all") } : null, + ].filter(Boolean) as { label: string; onRemove: () => void }[]; + return (
- - + { setSearchQuery(""); setActiveTab("all"); }} + useFilterToggle={false} + showDayFilter={false} + showSortButton={false} + /> +
); }; -export default Programm; \ No newline at end of file +export default Speaker;