adjust filter in Programm page
This commit is contained in:
parent
59a03518b5
commit
f6db9f6134
BIN
relume-test/src/assets/speakers/speaker5.jpg
Normal file
BIN
relume-test/src/assets/speakers/speaker5.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 95 KiB |
BIN
relume-test/src/assets/speakers/speaker6.jpg
Normal file
BIN
relume-test/src/assets/speakers/speaker6.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
@ -55,31 +55,31 @@ export const Banner15 = (props: Banner15Props) => {
|
||||
export const Banner15Defaults: Props = {
|
||||
sections: [
|
||||
{
|
||||
title: "Relume Library",
|
||||
title: "UX Schweiz",
|
||||
image: {
|
||||
src: "https://relume-assets.s3.us-east-1.amazonaws.com/placeholder-image.svg",
|
||||
alt: "Relume Library",
|
||||
alt: "UX Schweiz",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Relume Library",
|
||||
title: "Adobe",
|
||||
image: {
|
||||
src: "https://relume-assets.s3.us-east-1.amazonaws.com/placeholder-image.svg",
|
||||
alt: "Relume Library",
|
||||
alt: "Adobe",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Relume Library",
|
||||
title: "Google",
|
||||
image: {
|
||||
src: "https://relume-assets.s3.us-east-1.amazonaws.com/placeholder-image.svg",
|
||||
alt: "Relume Library",
|
||||
alt: "Google",
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Relume Library",
|
||||
title: "Figma",
|
||||
image: {
|
||||
src: "https://relume-assets.s3.us-east-1.amazonaws.com/placeholder-image.svg",
|
||||
alt: "Relume Library",
|
||||
alt: "Figma",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@ -23,30 +23,29 @@ type Props = {
|
||||
};
|
||||
|
||||
const levelStyles: Record<EventLevel, string> = {
|
||||
Senior: "bg-electric-violet text-cloud-white",
|
||||
"Mid-Level": "bg-electric-violet text-cloud-white",
|
||||
Junior: "bg-acid-lime text-tech-navy",
|
||||
Senior: "bg-electric-violet/20 text-electric-violet",
|
||||
"Mid-Level": "bg-electric-violet/20 text-electric-violet",
|
||||
Junior: "bg-electric-violet/20 text-electric-violet",
|
||||
};
|
||||
|
||||
const EventRow = ({ event }: { event: ProgramEvent }) => (
|
||||
<div className="grid grid-cols-1 items-center gap-4 border-t border-gray-200 py-6 last-of-type:border-b md:grid-cols-[5.5rem_5.5rem_1fr_max-content_max-content_max-content] md:gap-6 md:py-7">
|
||||
<div className="grid grid-cols-1 items-center gap-4 border-t border-gray-200 py-6 last-of-type:border-b md:grid-cols-[5.5rem_5.5rem_1fr_max-content_max-content] md:gap-6 md:py-7">
|
||||
<span className="text-sm font-semibold text-tech-navy">{event.time}</span>
|
||||
|
||||
<div className="w-full aspect-square md:w-[5.5rem]">
|
||||
<div className="w-14 h-14 shrink-0 md:w-[5.5rem] md:h-[5.5rem]">
|
||||
<img src={event.image.src} alt={event.image.alt} className="size-full object-cover" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<a href={`/programm/${event.id}`} className="hover:underline">
|
||||
<h5 className="text-lg font-bold text-tech-navy">{event.title}</h5>
|
||||
</a>
|
||||
<p className="text-sm text-gray-500">{event.speaker}</p>
|
||||
<span className={`w-fit rounded-md px-3 py-1 text-xs font-semibold ${levelStyles[event.level]}`}>
|
||||
{event.level}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<span className={`justify-self-start rounded-full px-3 py-1 text-xs font-semibold md:justify-self-auto ${levelStyles[event.level]}`}>
|
||||
{event.level}
|
||||
</span>
|
||||
|
||||
<a
|
||||
href={event.speakerUrl}
|
||||
className="shrink-0 rounded-full border border-electric-violet px-4 py-1.5 text-sm font-semibold text-electric-violet hover:bg-electric-violet hover:text-cloud-white transition-colors"
|
||||
|
||||
@ -2,7 +2,6 @@ import { useState } from "react";
|
||||
import { BiFilter, BiSearch } from "react-icons/bi";
|
||||
import { IoCloseOutline } from "react-icons/io5";
|
||||
import { RxChevronDown } from "react-icons/rx";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
|
||||
export type FilterTab = { value: string; label: string };
|
||||
type ActiveTag = { label: string; onRemove: () => void };
|
||||
@ -58,16 +57,18 @@ export const Filters5 = ({
|
||||
activeDay = "all",
|
||||
onDayChange = () => {},
|
||||
}: Props) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [sortOpen, setSortOpen] = useState(false);
|
||||
const [dayOpen, setDayOpen] = useState(false);
|
||||
|
||||
const TabRow = () => (
|
||||
const selectedDayLabel = DAYS.find((d) => d.value === activeDay)?.label ?? "Alle Tage";
|
||||
|
||||
const TabPills = () => (
|
||||
<div className="flex items-center gap-1 overflow-x-auto no-scrollbar">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.value}
|
||||
onClick={() => onTabChange(tab.value)}
|
||||
className={`shrink-0 rounded-full px-4 py-1.5 text-sm font-semibold transition-colors ${
|
||||
className={`shrink-0 rounded-2xl px-4 py-2 text-base font-semibold transition-colors ${
|
||||
activeTab === tab.value
|
||||
? "bg-electric-violet text-cloud-white"
|
||||
: "text-tech-navy hover:text-electric-violet"
|
||||
@ -79,46 +80,44 @@ export const Filters5 = ({
|
||||
</div>
|
||||
);
|
||||
|
||||
const SearchRow = () => (
|
||||
<div className="mt-4">
|
||||
<div className="mb-1 flex items-center justify-between">
|
||||
<span className="text-sm font-semibold text-tech-navy">{searchLabel}</span>
|
||||
<button
|
||||
onClick={onReset}
|
||||
className="text-sm font-semibold text-electric-violet underline"
|
||||
>
|
||||
{resetLabel}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 rounded-full border border-electric-violet px-4 py-2 text-sm">
|
||||
<BiSearch className="size-4 shrink-0 text-gray-400" />
|
||||
<input
|
||||
className="flex-1 bg-transparent outline-none placeholder-gray-400 text-sm"
|
||||
placeholder="Suchen..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Speaker variant: always-visible search, no toggle
|
||||
// Speaker page: just tabs + search, no day filter or sort
|
||||
if (!useFilterToggle) {
|
||||
return (
|
||||
<section className="px-[5%] py-8 bg-cloud-white font-barlow border-b border-gray-100">
|
||||
<div className="container">
|
||||
<TabRow />
|
||||
<SearchRow />
|
||||
<section className="bg-cloud-white font-barlow border-b border-gray-200 px-[5%] py-8">
|
||||
<div className="container flex flex-col gap-6">
|
||||
<TabPills />
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-base font-medium text-tech-navy">{searchLabel}</span>
|
||||
<button onClick={onReset} className="text-base font-medium text-tech-navy">
|
||||
{resetLabel}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex h-12 items-center gap-3 rounded-2xl border border-electric-violet px-3">
|
||||
<BiSearch className="size-6 shrink-0 text-electric-violet" />
|
||||
<input
|
||||
className="flex-1 bg-transparent text-base text-electric-violet outline-none placeholder:text-electric-violet/50"
|
||||
placeholder="Suchen..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button onClick={() => onSearchChange("")}>
|
||||
<IoCloseOutline className="size-5 text-electric-violet" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{activeTags.length > 0 && (
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{activeTags.map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="flex items-center gap-1 rounded-full border border-electric-violet px-3 py-1 text-sm font-semibold text-electric-violet"
|
||||
className="flex items-center gap-2 rounded-lg bg-neutral-dark pl-4 pr-3 py-2 text-base text-cloud-white"
|
||||
>
|
||||
{tag.label}
|
||||
<button onClick={tag.onRemove}>
|
||||
<IoCloseOutline className="size-4" />
|
||||
<IoCloseOutline className="size-[18px]" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
@ -129,36 +128,39 @@ export const Filters5 = ({
|
||||
);
|
||||
}
|
||||
|
||||
// Programm variant: toggle button, collapsible panel
|
||||
// Programm page: full filter bar, always visible
|
||||
return (
|
||||
<section className="px-[5%] py-8 bg-cloud-white font-barlow border-b border-gray-100">
|
||||
<div className="container">
|
||||
<div className="flex items-center gap-4 overflow-x-auto no-scrollbar">
|
||||
<button
|
||||
onClick={() => setIsOpen((v) => !v)}
|
||||
className="flex shrink-0 items-center gap-2 rounded-full bg-electric-violet px-4 py-2 text-sm font-semibold text-cloud-white"
|
||||
>
|
||||
<BiFilter className="size-5" />
|
||||
<section className="bg-cloud-white font-barlow px-[5%] py-8">
|
||||
<div className="container flex flex-col gap-8">
|
||||
|
||||
{/* Row 1: Filter label + category pills + sort */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex shrink-0 items-center gap-3 rounded-2xl bg-electric-violet px-6 py-3 text-base font-semibold text-cloud-white">
|
||||
<BiFilter className="size-6" />
|
||||
Filter
|
||||
</button>
|
||||
<TabRow />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-1 items-center overflow-x-auto no-scrollbar">
|
||||
<TabPills />
|
||||
</div>
|
||||
|
||||
{showSortButton && (
|
||||
<div className="relative ml-auto shrink-0">
|
||||
<div className="relative shrink-0">
|
||||
<button
|
||||
onClick={() => setSortOpen((v) => !v)}
|
||||
className="flex items-center gap-1 text-sm font-semibold text-electric-violet"
|
||||
className="flex items-center gap-2 text-base font-semibold text-electric-violet"
|
||||
>
|
||||
Sortieren{" "}
|
||||
Sortieren
|
||||
<RxChevronDown
|
||||
className={`transition-transform ${sortOpen ? "rotate-180" : ""}`}
|
||||
className={`size-6 transition-transform ${sortOpen ? "rotate-180" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
{sortOpen && (
|
||||
<div className="absolute right-0 top-8 z-50 w-44 border border-gray-200 bg-white shadow-sm">
|
||||
<div className="absolute right-0 top-9 z-50 w-48 overflow-hidden rounded-xl border border-gray-200 bg-white shadow-md">
|
||||
{["Zeit aufsteigend", "Zeit absteigend"].map((opt) => (
|
||||
<button
|
||||
key={opt}
|
||||
className="block w-full px-4 py-2 text-left text-sm hover:bg-gray-50"
|
||||
className="block w-full px-4 py-2.5 text-left text-sm text-tech-navy hover:bg-electric-violet/10"
|
||||
onClick={() => setSortOpen(false)}
|
||||
>
|
||||
{opt}
|
||||
@ -170,68 +172,89 @@ export const Filters5 = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<AnimatePresence initial={false}>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
initial={{ height: 0, opacity: 0 }}
|
||||
animate={{ height: "auto", opacity: 1 }}
|
||||
exit={{ height: 0, opacity: 0 }}
|
||||
transition={{ duration: 0.2, ease: "easeInOut" }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="mt-4 flex flex-wrap items-end gap-4">
|
||||
<div className="flex min-w-[220px] flex-1 items-center gap-2 rounded-full border border-electric-violet px-4 py-2 text-sm">
|
||||
<BiSearch className="size-4 shrink-0 text-gray-400" />
|
||||
<input
|
||||
className="flex-1 bg-transparent outline-none placeholder-gray-400 text-sm"
|
||||
placeholder={searchLabel}
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={onReset}
|
||||
className="shrink-0 text-sm font-semibold text-electric-violet underline"
|
||||
>
|
||||
{resetLabel}
|
||||
{/* Row 2: Search + Day filter — always visible */}
|
||||
<div className="grid grid-cols-1 gap-8 md:grid-cols-2">
|
||||
|
||||
{/* Search */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-base font-medium text-tech-navy">{searchLabel}</span>
|
||||
<button onClick={onReset} className="text-base font-medium text-tech-navy">
|
||||
{resetLabel}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex h-12 items-center gap-3 rounded-2xl border border-electric-violet px-3">
|
||||
<BiSearch className="size-6 shrink-0 text-electric-violet" />
|
||||
<input
|
||||
className="flex-1 bg-transparent text-base text-electric-violet outline-none placeholder:text-electric-violet/50"
|
||||
placeholder="Titel oder Speaker..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => onSearchChange(e.target.value)}
|
||||
/>
|
||||
{searchQuery && (
|
||||
<button onClick={() => onSearchChange("")}>
|
||||
<IoCloseOutline className="size-5 text-electric-violet" />
|
||||
</button>
|
||||
{showDayFilter && (
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-xs font-semibold text-gray-500">
|
||||
Filtern nach Eventtage
|
||||
</span>
|
||||
<select
|
||||
value={activeDay}
|
||||
onChange={(e) => onDayChange(e.target.value)}
|
||||
className="rounded-full border border-electric-violet px-4 py-2 text-sm font-semibold text-tech-navy outline-none"
|
||||
>
|
||||
{DAYS.map((d) => (
|
||||
<option key={d.value} value={d.value}>
|
||||
{d.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Day dropdown */}
|
||||
{showDayFilter && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-base font-medium text-tech-navy">Filtern nach Eventtage</span>
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setDayOpen((v) => !v)}
|
||||
className="flex h-12 w-full items-center justify-between gap-4 rounded-2xl border border-electric-violet px-4 text-base text-electric-violet"
|
||||
>
|
||||
<span>{selectedDayLabel}</span>
|
||||
<RxChevronDown
|
||||
className={`size-6 shrink-0 transition-transform ${dayOpen ? "rotate-180" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
{dayOpen && (
|
||||
<div className="absolute left-0 top-[calc(100%+4px)] z-50 w-full overflow-hidden rounded-2xl border border-electric-violet bg-white shadow-md">
|
||||
{DAYS.map((d) => (
|
||||
<button
|
||||
key={d.value}
|
||||
className={`block w-full px-4 py-2.5 text-left text-base transition-colors hover:bg-electric-violet/10 ${
|
||||
activeDay === d.value
|
||||
? "font-semibold text-electric-violet"
|
||||
: "text-tech-navy"
|
||||
}`}
|
||||
onClick={() => {
|
||||
onDayChange(d.value);
|
||||
setDayOpen(false);
|
||||
}}
|
||||
>
|
||||
{d.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{activeTags.length > 0 && (
|
||||
<div className="mt-3 flex flex-wrap gap-2">
|
||||
{activeTags.map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="flex items-center gap-1 rounded-full border border-electric-violet px-3 py-1 text-sm font-semibold text-electric-violet"
|
||||
>
|
||||
{tag.label}
|
||||
<button onClick={tag.onRemove}>
|
||||
<IoCloseOutline className="size-4" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</motion.div>
|
||||
</div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* Active tags */}
|
||||
{activeTags.length > 0 && (
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{activeTags.map((tag, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="flex items-center gap-2 rounded-lg bg-neutral-dark pl-4 pr-3 py-2 text-base text-cloud-white"
|
||||
>
|
||||
{tag.label}
|
||||
<button onClick={tag.onRemove}>
|
||||
<IoCloseOutline className="size-[18px]" />
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@ -2,6 +2,8 @@ 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 speaker5 from "../assets/speakers/speaker5.jpg";
|
||||
import speaker6 from "../assets/speakers/speaker6.jpg";
|
||||
import { BiLogoLinkedinSquare } from "react-icons/bi";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
@ -173,7 +175,7 @@ export const Team4Defaults: Props = {
|
||||
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
|
||||
},
|
||||
{
|
||||
image: { src: speaker1, alt: "Liam O'Connor" },
|
||||
image: { src: speaker5, alt: "Liam O'Connor" },
|
||||
name: "Liam O'Connor",
|
||||
role: "Lead Product Designer",
|
||||
company: "Spotify",
|
||||
@ -188,7 +190,7 @@ export const Team4Defaults: Props = {
|
||||
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
|
||||
},
|
||||
{
|
||||
image: { src: speaker2, alt: "Fabienne Keller" },
|
||||
image: { src: speaker6, alt: "Fabienne Keller" },
|
||||
name: "Fabienne Keller",
|
||||
role: "Gründerin",
|
||||
company: "User-First Agency",
|
||||
@ -202,36 +204,6 @@ export const Team4Defaults: Props = {
|
||||
talks: [{ title: "Workshop: Research Repositories", type: "Workshop" }],
|
||||
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
|
||||
},
|
||||
{
|
||||
image: { src: speaker3, alt: "Thomas Meyer" },
|
||||
name: 'Thomas "Tom" Meyer',
|
||||
role: "Creative Director",
|
||||
company: "Swisscom",
|
||||
slug: "thomas-meyer",
|
||||
location: "Bern, Schweiz",
|
||||
description:
|
||||
"Tom zeigt, wie man in großen Konzernen eine Design-Kultur etabliert, die über das visuelle Layer hinausgeht.",
|
||||
fullBio:
|
||||
'Thomas "Tom" Meyer ist Creative Director bei Swisscom und zeigt, wie man in großen Konzernen eine Design-Kultur etabliert, die über das visuelle Layer hinausgeht. Er bringt über 15 Jahre Erfahrung im Corporate Design Management mit.',
|
||||
category: "Strategie",
|
||||
talks: [{ title: "Networking: Leading Teams", type: "Networking" }],
|
||||
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
|
||||
},
|
||||
{
|
||||
image: { src: speaker4, alt: "Sarah M. Widmer" },
|
||||
name: "Sarah M. Widmer",
|
||||
role: "Senior UX Architect",
|
||||
company: "SBB",
|
||||
slug: "sarah-m-widmer",
|
||||
location: "Bern, Schweiz",
|
||||
description:
|
||||
"Sarah gestaltet die Mobilität von morgen. Sie zeigt, wie man Millionen von Nutzern barrierefrei durch den digitalen öV-Dschungel leitet.",
|
||||
fullBio:
|
||||
"Sarah M. Widmer ist Senior UX Architect bei der SBB und gestaltet die Mobilität von morgen. Sie zeigt, wie man Millionen von Nutzern barrierefrei durch den digitalen öV-Dschungel leitet und dabei Accessibility nicht als Pflicht, sondern als Designprinzip versteht.",
|
||||
category: "Design / UX",
|
||||
talks: [{ title: "Talk: Inclusive Design at Scale", type: "Talk" }],
|
||||
socialLinks: [{ href: "#", icon: <BiLogoLinkedinSquare className="size-6" /> }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@ -2,6 +2,8 @@ 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";
|
||||
import spk5 from "../assets/speakers/speaker5.jpg";
|
||||
import spk6 from "../assets/speakers/speaker6.jpg";
|
||||
import type { ProgramEvent } from "../components/Event33";
|
||||
|
||||
export const ALL_EVENTS: ProgramEvent[] = [
|
||||
@ -69,7 +71,7 @@ export const ALL_EVENTS: ProgramEvent[] = [
|
||||
{
|
||||
id: "do-5",
|
||||
time: "18:00 – 19:30 Uhr",
|
||||
image: { src: spk1, alt: "Liam O'Connor" },
|
||||
image: { src: spk5, alt: "Liam O'Connor" },
|
||||
title: "Talk: Ethical AI",
|
||||
subtitle: "Verantwortungsvolles Design in der KI-Ära",
|
||||
speaker: "Liam O'Connor",
|
||||
@ -145,7 +147,7 @@ export const ALL_EVENTS: ProgramEvent[] = [
|
||||
{
|
||||
id: "fr-5",
|
||||
time: "18:00 – 19:30 Uhr",
|
||||
image: { src: spk1, alt: "Liam O'Connor" },
|
||||
image: { src: spk5, alt: "Liam O'Connor" },
|
||||
title: "Talk: Trust in AI",
|
||||
subtitle: "Wie wir Vertrauen in algorithmische Systeme aufbauen",
|
||||
speaker: "Liam O'Connor",
|
||||
@ -160,7 +162,7 @@ export const ALL_EVENTS: ProgramEvent[] = [
|
||||
{
|
||||
id: "fr-6",
|
||||
time: "20:00 – 21:30 Uhr",
|
||||
image: { src: spk2, alt: "Fabienne Keller" },
|
||||
image: { src: spk6, alt: "Fabienne Keller" },
|
||||
title: "Workshop: Research Repositories",
|
||||
subtitle: "Wissen organisieren, das wirklich genutzt wird",
|
||||
speaker: "Fabienne Keller",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user