Met en place l'affichage des billets dans MenuPanier ainsi que la gestion de leur suppresion et ajout : import { motion } from 'framer-motion'
import React from 'react'
import { Link } from 'react-router-dom'
import {useState} from 'react';
import TextField from '../../../form/TextField';
import Button from '../../../form/Button';
import ItemPanier from './ItemPanier';

type Props = {
  isOpen: boolean;
  setIsOpen: (isOpen : boolean) => void;
}

export default function MenuConnexion(props: Props) {

  const menuVariants = {
    hidden:{
      x: "42rem",
      transition:{
        duration: 0.5,
        ease: [1, -0.02, 0,1]
      }
    },
    visible:{
      x: 0,
      transition:{
        duration: 0.5,
        ease: [1, -0.02, 0,1]
      }
    }
  }

  return (
    <motion.div className='side-menu cart'
    variants={menuVariants}
    initial="hidden"
    animate={props.isOpen ? "visible" : "hidden"}>
        <div className="cross" onClick={() => props.setIsOpen(false)}>
            <svg width="36" height="28" viewBox="0 0 36 28" fill="none" xmlns="http://www.w3.org/2000/svg">
              <rect x="6.52539" y="0.321533" width="35.8974" height="3.58974" rx="1.79487" transform="rotate(45 6.52539 0.321533)" fill="#E45A3B"/>
              <rect x="3.87891" y="25.5957" width="35.8974" height="3.58974" rx="1.79487" transform="rotate(-45 3.87891 25.5957)" fill="#E45A3B"/>
            </svg>
        </div>
        <div className="container">
          <h2>Mon panier</h2>

          <section className='le-panier'>
            <ItemPanier typeBillet={1} quantite={1}/>
            <ItemPanier typeBillet={1} quantite={1}/>
            
          </section>
          
          <div className="sous-total">
            <h4>Sous total: </h4>
            <h4>35,99€</h4>
          </div>
          <Button text="RESERVER"/>
        </div>
    </motion.div>
  )
}
import React, { useContext } from 'react'
import {useState} from 'react';
import { getCookie, setCookie } from '../../../../cookies/CookiesLib.tsx';
import { useEffect } from 'react';
import { CartContext } from '../../../../App.tsx';
import TicketCard from '../../../TicketCard.tsx';

type Props = {
    typeBillet: number,
    quantite: number,
}

export default function ItemPanier(props: Props) {
    const [quantite, setQuantite] = useState(props.quantite);
    const { cart, setCart } = useContext(CartContext);

    
    const updateCartCookie = (typeBillet: number, newQuantite: number) =>{
        const cart = getCookie("cart");
        const itemIndex = cart.findIndex((item: { typeBillet: number; }) => item.typeBillet === typeBillet);
        
        if(itemIndex > -1){
            cart[itemIndex].quantite = newQuantite;
        }else{
            cart.push({typeBillet, quantite: newQuantite});
        }
        
        const filteredCart = cart.filter((item: { quantite: number; }) => item.quantite > 0);
        setCookie('cart', filteredCart, {expires: 7, sameSite: 'None', secure: true});
        
        useEffect(() => {
            updateCartCookie(props.typeBillet, quantite);
        }, [quantite]);
        console.log(cart);
    }
    
    const removeFromCart = () => {
       const cart = getCookie('cart');
       const newCart = cart.filter((item: { typeBillet: number; }) => item.typeBillet !== props.typeBillet);
       setCookie('cart', newCart, { expires: 7 });
    };

  return (
    <div className="item-panier">
        <div className="container-item-panier">
            <div className="container-image">
                <img src="/images/billet.png" alt="billet" />
            </div>
            <div className="informations">
                <div className="textes">

            <div>
              <h2>Mon Panier</h2>
              {!cart.length && <p>Votre panier est vide.</p>}
              <ul>
                  <li key={index}>
                    <div>ID du billet: {billet.id}</div>
                    <div>Titre: {billet.title}</div>
                    <div>Prix: {billet.price}€</div>
                    <div>Quantité: {billet.quantity}</div>
                    {/* Si le billet est un forfait, afficher les jours sélectionnés */}
                    {billet.selectedDays && (
                      <div>
                        Jours sélectionnés:
                        {billet.selectedDays.map((day, dayIndex) => (
                          <span key={dayIndex}> {day}</span>
                      </div>
                    )}
                  </li>
              </ul>
            </div>
            );
                </div>
                <div className="compteur-quantitee">
                    <span className='ajout-retrait' onClick={() => setQuantite(quantite-1)}>-</span>
                    <input type="number" className='quantite' value={quantite}/>
                    <span className='ajout-retrait' onClick={() => setQuantite(quantite+1)}>+</span>
                </div>
            </div>
        </div>   
        <img className='cross' src="/icones/cross.svg" alt="croix" onClick={removeFromCart}/>
    </div>
  )
}
import './index.css'
import './components/nav/Navbar'
import Navbar from './components/nav/Navbar'
import { AnimatePresence } from 'framer-motion'
import { Route, Routes, useLocation } from 'react-router-dom'
import Accueil from './pages/Accueil/Accueil'
import { createContext, useEffect, useState } from 'react'
import Programmation from './pages/Programmation/Programmation'
import PageArtiste from './pages/Artiste/PageArtiste'
import Billeterie from './pages/Billeterie/Billeterie'
import Faq from './pages/faq/Faq'
import { getCookie } from './cookies/CookiesLib'
  
interface cartContext{
  cart: any[],
  setCart: (cart: any[]) => void,
}


export const CartContext = createContext<cartContext>({
  cart: [],
  setCart: () => {},
})

function App() {
  const [cart, setCart] = useState<any[]>(() => getCookie('cart') || []);
  const [isNavInFocus, setIsNavInFocus] = useState(false)
  const[isNavTransparent, setIsNavTransparent] = useState(true);
  const location = useLocation();
  

  useEffect(() => {

  }, []);


  return (
    <CartContext.Provider value = {{cart, setCart}}>
      <Navbar setNavInFocus={setIsNavInFocus} isTransparent={isNavTransparent}/>
      <AnimatePresence>

        <Routes location={location} key={location.key}>
            <Route path="/" element={<Accueil isNavInFocus={isNavInFocus} setIsNavTransparent={setIsNavTransparent}  />} />
            <Route path="/programmation" element={<Programmation isNavInFocus={isNavInFocus} setIsNavTransparent={setIsNavTransparent} />} />
            <Route path="/billeterie" element={<Billeterie isNavInFocus={isNavInFocus} setIsNavTransparent={setIsNavTransparent} />} />
            <Route path="/faq" element={<Faq isNavInFocus={isNavInFocus} setIsNavTransparent={setIsNavTransparent} />}/>
            <Route path="/artiste" element={<PageArtiste />}/>
        </Routes>

      </AnimatePresence>
    </CartContext.Provider>
  )
}

export default App
import { useEffect } from "react";
import TicketCard from "../../components/TicketCard";
import Footer from "../../components/footer";
import axios from 'axios';

type Props = {
  isNavInFocus: boolean;
  setIsNavTransparent: (isNavTransparent: boolean) => void;
};

type Billet = {
  idB: number;
  title: string;
  prix: number|string;
  nbTicket: number;
};



export default function Billeterie(props: Props) {
  const billets: Billet[] = [
    {
      idB: 1,
      title: "Accès Samedi 20 Juillet",
      prix: 60,
      nbTicket: 0,
    },
    {
      idB: 2,
      title: "Accès Dimanche 21 Juillet",
      prix: 80,
      nbTicket: 0,
    },
    {
      idB: 3,
      title: "Accès Lundi 22 Juillet",
      prix: 90,
      nbTicket: 0,
    },
    {
      idB: 4,
      title: "Forfait 2 jours",
      prix: "À partir de 140",
      nbTicket: 0,
    },
    {
      idB: 5,
      title: "Forfait 3 jours",
      prix: "180",
      nbTicket: 0,
    },
  ];

  useEffect(() => {
    window.scrollTo(0, 0);
    props.setIsNavTransparent(false);
  }, []);

  return (
    <>
      <div className="page-defaut" id="Billeterie">
        <header>
          <img
            src="images/bgbilletterie.png"
            alt="bgbilleterie"
            className="bgbillet"
          />
          <div className="header">
            <h2>BILLETERIE</h2>
          </div>
          <img
            src="images/billet_pass1j.png"
            alt="bgbilleterie"
            className="billetExemple"
          ></img>
        </header>

        <main className="billets">
          <section className="header-billet">
            <div>
              <p>samedi 20 Juillet 2024 - lundi 22 Juillet 2024</p>
              <h2>FESTI'IUTO ÉDITION 2024</h2>
              <div className="lieu">
                <svg
                  xmlns="http://www.w3.org/2000/svg"
                  width="14"
                  height="20"
                  viewBox="0 0 14 20"
                  fill="none"
                >
                  <path
                    d="M7 0C3.13 0 0 3.13 0 7C0 12.25 7 20 7 20C7 20 14 12.25 14 7C14 3.13 10.87 0 7 0ZM7 9.5C6.33696 9.5 5.70107 9.23661 5.23223 8.76777C4.76339 8.29893 4.5 7.66304 4.5 7C4.5 6.33696 4.76339 5.70107 5.23223 5.23223C5.70107 4.76339 6.33696 4.5 7 4.5C7.66304 4.5 8.29893 4.76339 8.76777 5.23223C9.23661 5.70107 9.5 6.33696 9.5 7C9.5 7.66304 9.23661 8.29893 8.76777 8.76777C8.29893 9.23661 7.66304 9.5 7 9.5Z"
                    fill="#4E4E4E"
                  />
                </svg>
                <p>3 rue Montorgueil, 45000, France</p>
              </div>
            </div>
            <div>
              <svg
                width="64"
                height="64"
                viewBox="0 0 64 64"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  d="M62.9991 27.739L42.1815 27.7675L56.8787 13.0286L50.7001 6.86056L36.0029 21.5994L35.9744 0.785744L27.2406 0.797718L27.2692 21.6114L12.5316 6.91288L6.36413 13.0979L21.1017 27.7964L0.289932 27.825L0.301899 36.5537L21.1137 36.5251L6.41646 51.2641L12.6009 57.4321L27.2981 42.6932L27.3266 63.5069L36.0603 63.4949L36.0318 42.6812L50.7694 57.3798L56.931 51.1948L42.1934 36.4962L63.011 36.4677L62.9991 27.739Z"
                  fill="#FFD600"
                />
              </svg>
            </div>
          </section>
          <section className="billets-content">
            <h3>Billets</h3>
            <div className="achat-billets">
            {billets.map((billet) => (
              <TicketCard
                key={billet.idB}
                id={billet.idB}
                title={billet.title}
                price={billet.prix}
                nbTicket={billet.nbTicket}
                isForfait={billet.idB === 4}
              />
            ))}
            </div>
          </section>
        </main>
      </div>
      <Footer />
    </>
  );
}
import { motion } from 'framer-motion';
import { useContext, useState } from 'react';
import Button from '../components/form/Button';
import { getCookie, setCookie } from '../cookies/CookiesLib.tsx';
import { CartContext } from '../App';


const initialDays = { '20Juillet': false, '21Juillet': false, '22Juillet': false };

type Props = {
  id: number;
  title: string;
  price: number|string;
  nbTicket: number;
  isForfait?: boolean; 
};

export default function TicketCard({ id, title, price, nbTicket, isForfait }: Props) {
  const [isOpen, setIsOpen] = useState(false);
  const [tickets, setTickets] = useState(nbTicket);
  const [rotation, setRotation] = useState(0);
  const [days, setDays] = useState(initialDays);
  const { cart, setCart } = useContext(CartContext);
  
  const handleTicketChange = (newTickets: number, event: React.MouseEvent) => {
    event.stopPropagation();
    setTickets(newTickets);
    };
  const addToCartHandler = () => {
    const selectedDays = isForfait ? 
      Object.entries(days).filter(([_, isChosen]) => isChosen).map(([day, _]) => day) : 
      [];

    const itemForCart = {
      id,
      title,
      price: typeof price === 'number' ? price : 0,
      quantity: tickets,
      selectedDays,
    };

    let newCart = cart.slice(); 
    const billetIndex = newCart.findIndex(billet => billet.id === itemForCart.id);
    if (billetIndex > -1) {
      newCart[billetIndex].quantity += itemForCart.quantity;
    } else {
      newCart.push(itemForCart);
    }
    setCart(newCart); // Met à jour l'état global du panier
    setCookie('cart', newCart, { expires: 7, sameSite: 'None', secure: true }); // Met à jour le cookie

  };


  const contentVariants = {
    closed: {
      opacity: 0,
      height: 0,
      overflow: 'hidden',
      transition: {
        duration: 0.2,
        ease: 'easeInOut',
        when: 'afterChildren',
      },
    },
    open: {
      opacity: 1,
      height: title === "Forfait 2 jours" ? 150 : 120,
      transition: {
        duration: 0.2,
        ease: 'easeInOut',
        when: 'beforeChildren',
      }
    },
  };

  const cardVariants = {
    hidden: { opacity: 0, y: 50 },
    visible: {
      opacity: 1,
      y: 0,
      transition: {
        type: 'spring',
        stiffness: 120,
      },
    },
  };

  const maxSelectedDays = 2;

  const selectedDayCount = () => {
    return Object.values(days).filter(Boolean).length;
  };

  const toggleDaySelection = (day: keyof typeof initialDays) => {
    setDays(prevDays => {
      const isSelected = prevDays[day];
      const count = selectedDayCount();
  
      if (!isSelected && count >= maxSelectedDays) {
        return prevDays;
      }
      
      return { ...prevDays, [day]: !prevDays[day] };
    });
  };
  
  return (
    <motion.div
      className="ticket-card"
      layout
      initial="hidden"
      animate="visible"
      variants={cardVariants}
      onClick={() => {
        setIsOpen(!isOpen);
        setRotation(rotation === 0 ? 90 : 0);
      }}
    >
      <div className="content">
        <div className='left-part'>
          <h4>{title}</h4>
          <p>Les tickets ne sont pas remboursables.</p>
          <p>Dernière entrée à 11H.</p>
        </div>
        <div className='right-part'>
          <p>{price}€</p>
          <motion.div className="svg-container" animate={{ rotate: rotation }}>
            <svg xmlns="http://www.w3.org/2000/svg" width="13" height="20" viewBox="0 0 13 20" fill="none">
              <path d="M2 18L10 10L2 2" stroke="#4E4E4E" strokeWidth="4" />
            </svg>
          </motion.div>
        </div>
      </div>
      <motion.div
  className={`sub-menu ${title === "Forfait 2 jours" ? "forfait-2j" : ""}`}
  variants={contentVariants}
  initial="closed"
  animate={isOpen ? "open" : "closed"}
  exit="closed"
>
  <div className='top-partsubmenu'>
        <div className='left-part-sub'>
        {isForfait && (Object.keys(days) as Array<keyof typeof days>).map(day => (
          <label key={day}>
            <input 
              type="checkbox"
              checked={days[day]}
              onChange={() => toggleDaySelection(day)}
            />
            {day}
          </label>
        ))}
          <div className='sub-menu-left-part'>
          <div className ="rect">
            <img src="images/billet_pass1j.png" alt="Billet pass 1 jour" />
          </div>
          <div className='article-select'>
          <svg xmlns="http://www.w3.org/2000/svg" width="22" height="21" viewBox="0 0 22 21" fill="none">
            <path d="M22 9.03848H14.6966L19.8599 4.10947L17.6953 2.04109L12.532 6.97007V0H9.46799V6.97007L4.30475 2.04109L2.13807 4.10947L7.30131 9.03848H0V11.9615H7.30131L2.13807 16.8906L4.30475 18.9589L9.46799 14.0299V21H12.532V14.0299L17.6953 18.9589L19.8599 16.8906L14.6966 11.9615H22V9.03848Z" fill="#FFD600"/>
          </svg>
          <p>x{tickets} Article(s) sélectionné(s)</p>
          </div>
          </div>
        <div className="ticket-control">
          <button onClick={(event) => handleTicketChange(Math.max(tickets - 1, 0), event)}>-</button>
          <span>{tickets}</span>
          <button className='sommeButton' onClick={(event) => handleTicketChange(tickets + 1, event)}>+</button>
        </div>
        </div>
      </div>
      <div className='delimiter-submenu'></div>
      <div className='bottom-partsubmenu'>
        <div className='bottom-part-left'>
        <p>Sous-total</p>
          <p>{tickets * price}€</p>
        </div>
        <button onClick={addToCartHandler}>AJOUTER AU PANIER</button>
      </div>

      </motion.div>
    </motion.div>
  );
}