From 3ed08a71a6df7055c61026de869497c666971071 Mon Sep 17 00:00:00 2001 From: Alisa Cantillo-Olsson Date: Wed, 22 Apr 2026 16:18:26 +0200 Subject: [PATCH] add tickets page components --- relume-test/src/App.tsx | 2 + relume-test/src/components/Header23.tsx | 3 +- relume-test/src/components/Navbar.tsx | 38 +++-- relume-test/src/components/Pricing1Ticket.tsx | 79 +++++++++++ .../src/components/Pricing39Ticket.tsx | 88 ++++++++++++ relume-test/src/pages/Tickets.tsx | 133 ++++++++++++++++++ 6 files changed, 331 insertions(+), 12 deletions(-) create mode 100644 relume-test/src/components/Pricing1Ticket.tsx create mode 100644 relume-test/src/components/Pricing39Ticket.tsx diff --git a/relume-test/src/App.tsx b/relume-test/src/App.tsx index 5f9426a..b281d53 100644 --- a/relume-test/src/App.tsx +++ b/relume-test/src/App.tsx @@ -2,6 +2,7 @@ import { Routes, Route } from "react-router-dom"; import Home from "./pages/Home"; import Programm from "./pages/Programm"; import Speaker from "./pages/Speaker"; +import Tickets from "./pages/Tickets"; function App() { return ( @@ -9,6 +10,7 @@ function App() { } /> } /> } /> + } /> ); } diff --git a/relume-test/src/components/Header23.tsx b/relume-test/src/components/Header23.tsx index affacd8..e63f75f 100644 --- a/relume-test/src/components/Header23.tsx +++ b/relume-test/src/components/Header23.tsx @@ -11,7 +11,7 @@ type Props = { export type Header23Props = React.ComponentPropsWithoutRef<"section"> & Partial; export const Header23 = (props: Header23Props) => { - const { heading, description, buttons } = { + const { heading, description, buttons, children } = { ...Header23Defaults, ...props, }; @@ -44,6 +44,7 @@ export const Header23 = (props: Header23Props) => { ))} )} + {children} diff --git a/relume-test/src/components/Navbar.tsx b/relume-test/src/components/Navbar.tsx index f8314cb..a5efd23 100644 --- a/relume-test/src/components/Navbar.tsx +++ b/relume-test/src/components/Navbar.tsx @@ -4,6 +4,9 @@ import { Button, useMediaQuery } from "@relume_io/relume-ui"; import type { ButtonProps } from "@relume_io/relume-ui"; import { AnimatePresence, motion } from "framer-motion"; import { RxChevronDown } from "react-icons/rx"; +import { Link } from "react-router-dom"; + +type NavButtonProps = ButtonProps & { url?: string }; type ImageProps = { url?: string; @@ -20,7 +23,7 @@ type NavLink = { type Props = { logo: ImageProps; navLinks: NavLink[]; - buttons: ButtonProps[]; + buttons: NavButtonProps[]; }; export type Navbar3Props = React.ComponentPropsWithoutRef<"section"> & Partial; @@ -89,11 +92,15 @@ export const Navbar3 = (props: Navbar3Props) => { ))}
- {buttons.map((button, index) => ( - - ))} + {buttons.map(({ url, ...button }, index) => + url ? ( + + + + ) : ( + + ) + )}
{isMobileMenuOpen && ( @@ -112,11 +119,19 @@ export const Navbar3 = (props: Navbar3Props) => {
- {buttons.map((button, index) => ( - - ))} + {buttons.map(({ url, ...button }, index) => + url ? ( + + + + ) : ( + + ) + )}
@@ -201,6 +216,7 @@ export const Navbar3Defaults: Props = { title: "Tickets", size: "sm", variant: "primary", + url: "/tickets", }, ], }; diff --git a/relume-test/src/components/Pricing1Ticket.tsx b/relume-test/src/components/Pricing1Ticket.tsx new file mode 100644 index 0000000..e20db1c --- /dev/null +++ b/relume-test/src/components/Pricing1Ticket.tsx @@ -0,0 +1,79 @@ +import React, { useState } from "react"; + +type TicketPlan = { + period: string; + price: number; + date: string; +}; + +type Props = { + heading: string; + plan: TicketPlan; +}; + +export type Pricing1TicketProps = React.ComponentPropsWithoutRef<"section"> & Partial; + +const TicketCardViolet = ({ plan }: { plan: TicketPlan }) => { + const [qty, setQty] = useState(0); + const total = qty * plan.price; + + return ( +
+

{plan.period}

+

CHF {plan.price}

+

{plan.date}

+
+
+ + {qty} + +
+
+
CHF {plan.price},-
+
{total},-
+
+
+ +
+ ); +}; + +export const Pricing1Ticket = (props: Pricing1TicketProps) => { + const { heading, plan } = { ...Pricing1TicketDefaults, ...props }; + + return ( +
+
+

+ {heading} +

+
+ +
+
+
+ ); +}; + +export const Pricing1TicketDefaults: Props = { + heading: "Earlybird", + plan: { + period: "DO – FR", + price: 250, + date: "25. – 26. Juni 2026", + }, +}; + +export default Pricing1Ticket; diff --git a/relume-test/src/components/Pricing39Ticket.tsx b/relume-test/src/components/Pricing39Ticket.tsx new file mode 100644 index 0000000..fe475c2 --- /dev/null +++ b/relume-test/src/components/Pricing39Ticket.tsx @@ -0,0 +1,88 @@ +import React, { useState } from "react"; + +type TicketPlan = { + day: string; + price: number; + date: string; +}; + +type Props = { + heading: string; + plans: TicketPlan[]; +}; + +export type Pricing39TicketProps = React.ComponentPropsWithoutRef<"section"> & Partial; + +const TicketCardLime = ({ plan }: { plan: TicketPlan }) => { + const [qty, setQty] = useState(0); + const total = qty * plan.price; + + return ( +
+

{plan.day}

+

CHF {plan.price}

+

{plan.date}

+
+
+ + {qty} + +
+
+
CHF {plan.price},-
+
{total},-
+
+
+ +
+ ); +}; + +export const Pricing39Ticket = (props: Pricing39TicketProps) => { + const { heading, plans } = { ...Pricing39TicketDefaults, ...props }; + + return ( +
+
+

+ {heading} +

+
+ {plans.map((plan, index) => ( + + ))} +
+
+
+ ); +}; + +export const Pricing39TicketDefaults: Props = { + heading: "1-Tagespässe", + plans: [ + { day: "Donnerstag", price: 131, date: "25. Juni 2026" }, + { day: "Freitag", price: 131, date: "26. Juni 2026" }, + ], +}; + +export default Pricing39Ticket; diff --git a/relume-test/src/pages/Tickets.tsx b/relume-test/src/pages/Tickets.tsx index e69de29..9682c29 100644 --- a/relume-test/src/pages/Tickets.tsx +++ b/relume-test/src/pages/Tickets.tsx @@ -0,0 +1,133 @@ +"use client"; + +import React, { useState, useEffect } from "react"; +import { Navbar3 } from "../components/Navbar"; +import { Header23 } from "../components/Header23"; +import { Pricing1Ticket } from "../components/Pricing1Ticket"; +import { Pricing39Ticket } from "../components/Pricing39Ticket"; +import { Footer3 } from "../components/Footer"; +import { Button, Input } from "@relume_io/relume-ui"; + +const COUNTDOWN_TARGET = "2026-06-25T09:00:00.000+02:00"; + +type CountdownValues = { days: string; hours: string; minutes: string; seconds: string }; + +const Countdown = ({ targetDate }: { targetDate: string }) => { + const [time, setTime] = useState({ + days: "00", + hours: "00", + minutes: "00", + seconds: "00", + }); + + useEffect(() => { + const target = new Date(targetDate).getTime(); + const pad = (n: number) => (n < 10 ? `0${n}` : `${n}`); + + const tick = () => { + const diff = target - Date.now(); + if (diff <= 0) { + setTime({ days: "00", hours: "00", minutes: "00", seconds: "00" }); + return; + } + setTime({ + days: pad(Math.floor(diff / 86400000)), + hours: pad(Math.floor((diff % 86400000) / 3600000)), + minutes: pad(Math.floor((diff % 3600000) / 60000)), + seconds: pad(Math.floor((diff % 60000) / 1000)), + }); + }; + + tick(); + const id = setInterval(tick, 1000); + return () => clearInterval(id); + }, [targetDate]); + + const Cell = ({ value, label }: { value: string; label: string }) => ( +
+ + {value} + + {label} +
+ ); + + const Divider = () =>
; + + return ( +
+ + + + + + + +
+ ); +}; + +const Tickets = () => { + const [emailInput, setEmailInput] = useState(""); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + console.log({ emailInput }); + }; + + return ( +
+ + +
+ DO – FR, 25. – 26. Juni 2026 +
+

+ 10 Spots left! +

+
+ +
+
+
+ setEmailInput(e.target.value)} + className="text-tech-navy" + /> + +
+

+ By clicking Get Updates you're confirming that you agree with our{" "} + Terms and Conditions. +

+
+
+ + + + +
+ ); +}; + +export default Tickets;