mirror of
https://github.com/Lapikud/tipilan.git
synced 2026-08-08 09:41:59 +00:00
feat: switchable ticket card (#132)
* update sponsor social media link (#131) * update sponsor social media link * add ingame to sponsors * feat: add switchable-ticket-card, supporter ticket --------- Co-authored-by: Katri <96204214+SwagMuffin88@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { getTranslations, setRequestLocale } from "next-intl/server";
|
||||
import SwitchableTicketCard from "@/components/SwitchableTicketCard";
|
||||
|
||||
interface TicketCardProps {
|
||||
title: string;
|
||||
@@ -12,6 +13,7 @@ interface TicketCardProps {
|
||||
buttonHref: string;
|
||||
backgroundImage?: string;
|
||||
backgroundOpacity?: number;
|
||||
blueTinge?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -24,6 +26,7 @@ function TicketCard({
|
||||
buttonHref,
|
||||
backgroundImage,
|
||||
backgroundOpacity = 40,
|
||||
blueTinge = false,
|
||||
className = "",
|
||||
}: TicketCardProps) {
|
||||
return (
|
||||
@@ -39,6 +42,9 @@ function TicketCard({
|
||||
style={{ opacity: backgroundOpacity / 100 }}
|
||||
/>
|
||||
)}
|
||||
{blueTinge && (
|
||||
<div className="absolute inset-0 bg-[#005A8C]/30" />
|
||||
)}
|
||||
<div className="relative z-10 flex flex-col h-full">
|
||||
<h2
|
||||
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2rem,1.5rem+2vw,3rem)] leading-none text-[#00A3E0] uppercase`}
|
||||
@@ -77,7 +83,7 @@ function TicketCard({
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Link href={buttonHref} target="_blank">
|
||||
<Link href={buttonHref} target="_blank" className="w-fit">
|
||||
<button
|
||||
className={`px-4 py-2 border-4 border-transparent bg-[#00A3E0] hover:bg-[#E5E5EE] text-[#0A121F] cursor-pointer ${vipnagorgialla.className} font-bold italic leading-none uppercase transition`}
|
||||
>
|
||||
@@ -101,18 +107,29 @@ export default async function Tickets({
|
||||
return (
|
||||
<div className="bg-[#0E0F19] min-h-0 flex flex-col flex-1">
|
||||
{/* 2x2 Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 md:grid-rows-2 auto-rows-fr pt-14 flex-1 min-h-0">
|
||||
{/* KÜLASTAJA PILET */}
|
||||
<TicketCard
|
||||
title={t("tickets.visitor.name")}
|
||||
subtitle={t("tickets.subtitle")}
|
||||
price={t("tickets.visitor.price")}
|
||||
features={t.raw("tickets.visitor.features")}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 lg:grid-rows-2 auto-rows-fr pt-14 flex-1 min-h-0">
|
||||
|
||||
{/* KÜLASTAJA / TOETAJA — switchable */}
|
||||
<SwitchableTicketCard
|
||||
optionA={{
|
||||
title: t("tickets.visitor.name"),
|
||||
subtitle: t("tickets.subtitle"),
|
||||
price: t("tickets.visitor.price"),
|
||||
features: t.raw("tickets.visitor.features"),
|
||||
}}
|
||||
optionB={{
|
||||
title: t("tickets.supporter.name"),
|
||||
subtitle: t("tickets.subtitle"),
|
||||
price: t("tickets.supporter.price"),
|
||||
features: t.raw("tickets.supporter.features"),
|
||||
}}
|
||||
buttonText={t("tickets.buyButton")}
|
||||
buttonHref="https://fienta.com/et/tipilan"
|
||||
backgroundImage="/images/landing/visitor_tournament.jpg"
|
||||
backgroundOpacity={30}
|
||||
className="border-b-[3px] md:border-b-[3px] md:border-r-[3px]"
|
||||
backgroundImageB="/images/landing/main_supporter.jpg"
|
||||
backgroundOpacityA={30}
|
||||
backgroundOpacityB={40}
|
||||
className="border-b-[3px] lg:border-r-[3px]"
|
||||
/>
|
||||
|
||||
{/* LAN PILET */}
|
||||
@@ -124,7 +141,7 @@ export default async function Tickets({
|
||||
buttonText={t("tickets.buyButton")}
|
||||
buttonHref="https://fienta.com/et/tipilan"
|
||||
backgroundImage="/images/landing/explore_teaser.png"
|
||||
backgroundOpacity={80}
|
||||
backgroundOpacity={100}
|
||||
className="border-b-[3px]"
|
||||
/>
|
||||
|
||||
@@ -137,7 +154,7 @@ export default async function Tickets({
|
||||
buttonText={t("tickets.buyButton")}
|
||||
buttonHref="https://fienta.com/et/tipilan"
|
||||
backgroundImage="/images/landing/league_ticket.jpg"
|
||||
className="border-b-[3px] md:border-b-0 md:border-r-[3px]"
|
||||
className="border-b-[3px] lg:border-b-0 lg:border-r-[3px]"
|
||||
/>
|
||||
|
||||
{/* CS2 TURNIIRI PILET */}
|
||||
|
||||
242
src/components/SwitchableTicketCard.tsx
Normal file
242
src/components/SwitchableTicketCard.tsx
Normal file
@@ -0,0 +1,242 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { vipnagorgialla } from "@/components/Vipnagorgialla";
|
||||
|
||||
interface TicketData {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
price: string;
|
||||
features: string[];
|
||||
}
|
||||
|
||||
interface SwitchableTicketCardProps {
|
||||
optionA: TicketData;
|
||||
optionB: TicketData;
|
||||
buttonText: string;
|
||||
buttonHref: string;
|
||||
backgroundImage?: string;
|
||||
backgroundImageB?: string;
|
||||
backgroundOpacity?: number;
|
||||
backgroundOpacityA?: number;
|
||||
backgroundOpacityB?: number;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function SwitchableTicketCard({
|
||||
optionA,
|
||||
optionB,
|
||||
buttonText,
|
||||
buttonHref,
|
||||
backgroundImage,
|
||||
backgroundImageB,
|
||||
backgroundOpacity = 40,
|
||||
backgroundOpacityA,
|
||||
backgroundOpacityB,
|
||||
className = "",
|
||||
}: SwitchableTicketCardProps) {
|
||||
const [isA, setIsA] = useState(true);
|
||||
|
||||
const titleOnRight = !isA;
|
||||
|
||||
function renderPrice(price: string) {
|
||||
return Array.from(price).map((char, index) => (
|
||||
<span
|
||||
key={`${char}-${index}`}
|
||||
className={
|
||||
char === "€" || char === "+"
|
||||
? "text-[#00A3E0]"
|
||||
: "text-[#EEE5E5]"
|
||||
}
|
||||
>
|
||||
{char}
|
||||
</span>
|
||||
));
|
||||
}
|
||||
|
||||
function renderFeatures(features: string[]) {
|
||||
return (
|
||||
<ul className="flex flex-col gap-2 grow">
|
||||
{features.map((feature, index) => (
|
||||
<li
|
||||
key={index}
|
||||
className={`flex items-start gap-2 text-[#EEE5E5] text-base ${titleOnRight ? "flex-row-reverse" : ""}`}
|
||||
>
|
||||
<span className="w-1 self-stretch min-h-5 bg-[#00A3E0] shrink-0" />
|
||||
<span className={titleOnRight ? "text-end" : ""}>{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative bg-[#0E0F19] border-[#1F5673] px-12 py-16 flex flex-col min-h-87.5 h-full overflow-hidden ${className}`}
|
||||
>
|
||||
{backgroundImage && (
|
||||
<Image
|
||||
src={backgroundImage}
|
||||
alt=""
|
||||
fill
|
||||
className="object-cover object-center transition-opacity duration-700 ease-out"
|
||||
style={{ opacity: isA ? (backgroundOpacityA ?? backgroundOpacity) / 100 : 0 }}
|
||||
/>
|
||||
)}
|
||||
{backgroundImageB && (
|
||||
<Image
|
||||
src={backgroundImageB}
|
||||
alt=""
|
||||
fill
|
||||
className="object-cover object-center transition-opacity duration-700 ease-out"
|
||||
style={{ opacity: isA ? 0 : (backgroundOpacityB ?? backgroundOpacity) / 100 }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="relative z-10 flex flex-col h-full">
|
||||
{/* Title + subtitle + price with position shift */}
|
||||
<div className="flex items-start mb-2 ">
|
||||
<div
|
||||
className="transition-[flex-grow] duration-700 ease-out "
|
||||
style={{ flexGrow: titleOnRight ? 1 : 0 }}
|
||||
/>
|
||||
<div className={`grid ${titleOnRight ? "text-end" : ""}`}>
|
||||
<div
|
||||
className="col-start-1 row-start-1 transition-opacity duration-500 ease-out"
|
||||
style={{ opacity: isA ? 1 : 0 }}
|
||||
>
|
||||
<h2
|
||||
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2rem,1.5rem+2vw,3rem)] leading-none text-[#00A3E0] uppercase`}
|
||||
>
|
||||
{optionA.title}
|
||||
</h2>
|
||||
<h3
|
||||
className={`${vipnagorgialla.className} font-bold italic text-2xl text-[#EEE5E5] uppercase`}
|
||||
>
|
||||
{optionA.subtitle}
|
||||
</h3>
|
||||
<p
|
||||
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+2vw,4rem)] leading-none mt-2`}
|
||||
>
|
||||
{renderPrice(optionA.price)}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className="col-start-1 row-start-1 transition-opacity duration-500 ease-out pointer-events-none"
|
||||
style={{ opacity: isA ? 0 : 1 }}
|
||||
>
|
||||
<h2
|
||||
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2rem,1.5rem+2vw,3rem)] leading-none text-[#00A3E0] uppercase`}
|
||||
>
|
||||
{optionB.title}
|
||||
</h2>
|
||||
<h3
|
||||
className={`${vipnagorgialla.className} font-bold italic text-2xl text-[#EEE5E5] uppercase`}
|
||||
>
|
||||
{optionB.subtitle}
|
||||
</h3>
|
||||
<p
|
||||
className={`${vipnagorgialla.className} font-bold italic text-[clamp(2.5rem,2rem+2vw,4rem)] leading-none mt-2`}
|
||||
>
|
||||
{renderPrice(optionB.price)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="transition-[flex-grow] duration-700 ease-out "
|
||||
style={{ flexGrow: titleOnRight ? 0 : 1 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative grow">
|
||||
<div
|
||||
className={`absolute inset-0 flex flex-col transition-all duration-700 ease-out ${
|
||||
isA
|
||||
? "opacity-100 translate-x-0"
|
||||
: "opacity-0 -translate-x-full"
|
||||
}`}
|
||||
>
|
||||
<div className="flex ">
|
||||
<div
|
||||
className="transition-[flex-grow] duration-700 ease-out "
|
||||
style={{ flexGrow: titleOnRight ? 1 : 0 }}
|
||||
/>
|
||||
<div>
|
||||
{renderFeatures(optionA.features)}
|
||||
</div>
|
||||
<div
|
||||
className="transition-[flex-grow] duration-700 ease-out "
|
||||
style={{ flexGrow: titleOnRight ? 0 : 1 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`absolute inset-0 flex flex-col transition-all duration-700 ease-out ${
|
||||
!isA
|
||||
? "opacity-100 translate-x-0"
|
||||
: "opacity-0 translate-x-full"
|
||||
}`}
|
||||
>
|
||||
<div className="flex ">
|
||||
<div
|
||||
className="transition-[flex-grow] duration-700 ease-out "
|
||||
style={{ flexGrow: titleOnRight ? 1 : 0 }}
|
||||
/>
|
||||
<div>
|
||||
{renderFeatures(optionB.features)}
|
||||
</div>
|
||||
<div
|
||||
className="transition-[flex-grow] duration-700 ease-out "
|
||||
style={{ flexGrow: titleOnRight ? 0 : 1 }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Link href={buttonHref} target="_blank" className="w-fit">
|
||||
<button
|
||||
className={`px-4 py-2 border-4 border-transparent bg-[#00A3E0] hover:bg-[#E5E5EE] text-[#0A121F] cursor-pointer ${vipnagorgialla.className} font-bold italic leading-none uppercase transition`}
|
||||
>
|
||||
{buttonText}
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Chevron buttons on left/right sides — hidden on mobile */}
|
||||
<button
|
||||
onClick={() => setIsA(false)}
|
||||
className={`${isA ? "lg:flex" : "hidden"} absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition-all duration-300 z-20`}
|
||||
aria-label={optionB.title}
|
||||
>
|
||||
<span className="material-symbols-outlined">chevron_right</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsA(true)}
|
||||
className={`${!isA ? "lg:flex" : "hidden"} absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 items-center justify-center bg-[#0E0F19]/50 hover:bg-[#007CAB] text-[#EEE5E5] transition-all duration-300 z-20`}
|
||||
aria-label={optionA.title}
|
||||
>
|
||||
<span className="material-symbols-outlined">chevron_left</span>
|
||||
</button>
|
||||
|
||||
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 flex justify-center gap-2 pb-2">
|
||||
<button
|
||||
onClick={() => setIsA(true)}
|
||||
className={`w-3 h-3 rounded-full transition ${
|
||||
isA ? "bg-[#00A3E0]" : "bg-[#1F5673] hover:bg-[#007CAB]/60"
|
||||
}`}
|
||||
aria-label={optionA.title}
|
||||
/>
|
||||
<button
|
||||
onClick={() => setIsA(false)}
|
||||
className={`w-3 h-3 rounded-full transition ${
|
||||
!isA ? "bg-[#00A3E0]" : "bg-[#1F5673] hover:bg-[#007CAB]/60"
|
||||
}`}
|
||||
aria-label={optionB.title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user