correction des bugue front end
This commit is contained in:
@@ -20,7 +20,7 @@ export default function Footer() {
|
||||
|
||||
|
||||
{/* Partie principale */}
|
||||
<div className="max-w-7xl mx-auto px-6 py-10 grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||
<div className="max-w-7xl mx-auto px-6 py-10 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-8">
|
||||
|
||||
|
||||
{/* Logo */}
|
||||
@@ -185,7 +185,7 @@ export default function Footer() {
|
||||
01 84 80 20 34
|
||||
</a>
|
||||
|
||||
<a href="mailto:contact@thetiptop.fr" className="inline-block mt-3 font-medium hover:text-teal-300">
|
||||
<a href="mailto:contact@thetiptop.fr" className="inline-block mt-3 font-medium break-all hover:text-teal-300">
|
||||
contact@thetiptop.fr
|
||||
</a>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function Header() {
|
||||
const navigate = useNavigate()
|
||||
const { user, logout } = useAuth()
|
||||
const isLoggedIn = !!user
|
||||
const logoContent = <img src={logo} alt="Logo de Thé Tip Top" className="h-10 sm:h-12 w-auto" />
|
||||
const logoContent = <img src={logo} alt="Logo de Thé Tip Top" className="h-7 sm:h-8 md:h-9 lg:h-12 w-auto" />
|
||||
|
||||
const handleLogout = () => {
|
||||
logout()
|
||||
@@ -40,12 +40,12 @@ export default function Header() {
|
||||
)}
|
||||
|
||||
{/* Nav desktop */}
|
||||
<nav className="hidden md:flex items-center gap-6">
|
||||
<nav className="hidden md:flex items-center gap-4 lg:gap-6">
|
||||
{navLinks.map((link) => (
|
||||
<Link
|
||||
key={link.to}
|
||||
to={link.to}
|
||||
className={`font-medium transition-colors ${
|
||||
className={`font-medium whitespace-nowrap transition-colors ${
|
||||
pathname === link.to ? 'text-[#006B6B]' : 'text-gray-700 hover:text-[#006B6B]'
|
||||
}`}
|
||||
>
|
||||
@@ -59,21 +59,21 @@ export default function Header() {
|
||||
<div className="w-8 h-8 rounded-full bg-[#006B6B] flex items-center justify-center text-white text-xs font-bold">
|
||||
{initials}
|
||||
</div>
|
||||
<span>{user.firstname}</span>
|
||||
<span className="whitespace-nowrap">{user.firstname}</span>
|
||||
</Link>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="text-sm text-gray-400 hover:text-red-500 transition-colors font-medium"
|
||||
className="text-sm text-gray-500 hover:text-red-500 transition-colors font-medium whitespace-nowrap"
|
||||
>
|
||||
Déconnexion
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<Link to="/connexion" className="bg-[#006B6B] text-white px-4 py-2 rounded-lg hover:bg-[#005a5a] transition-colors text-sm">
|
||||
<Link to="/connexion" className="bg-[#006B6B] border border-transparent text-white px-4 py-2 rounded-lg hover:bg-[#005a5a] transition-colors text-sm whitespace-nowrap">
|
||||
Connexion
|
||||
</Link>
|
||||
<Link to="/inscription" className="border border-[#006B6B] text-[#006B6B] px-4 py-2 rounded-lg hover:bg-[#006B6B] hover:text-white transition-colors text-sm">
|
||||
<Link to="/inscription" className="border border-[#006B6B] text-[#006B6B] px-4 py-2 rounded-lg hover:bg-[#006B6B] hover:text-white transition-colors text-sm whitespace-nowrap">
|
||||
Inscription
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ export default function UserNav({ active }) {
|
||||
key: 'code',
|
||||
to: '/saisir-code-unique',
|
||||
label: 'Saisir un code',
|
||||
shortLabel: 'Code',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 5v2m0 4v2m0 4v2M5 5a2 2 0 00-2 2v3a2 2 0 110 4v3a2 2 0 002 2h14a2 2 0 002-2v-3a2 2 0 110-4V7a2 2 0 00-2-2H5z" />
|
||||
@@ -16,6 +17,7 @@ export default function UserNav({ active }) {
|
||||
key: 'gains',
|
||||
to: '/mes-gains',
|
||||
label: 'Mes gains',
|
||||
shortLabel: 'Mes gains',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M20 12V22H4V12M22 7H2v5h20V7zM12 22V7M12 7H7.5a2.5 2.5 0 010-5C11 2 12 7 12 7zM12 7h4.5a2.5 2.5 0 000-5C13 2 12 7 12 7z" />
|
||||
@@ -26,6 +28,7 @@ export default function UserNav({ active }) {
|
||||
key: 'profile',
|
||||
to: '/profile',
|
||||
label: 'Profil',
|
||||
shortLabel: 'Profil',
|
||||
icon: (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
||||
@@ -43,12 +46,12 @@ export default function UserNav({ active }) {
|
||||
className={`flex-1 flex items-center justify-center gap-1.5 py-3 text-xs sm:text-sm font-medium transition-colors border-b-2 ${
|
||||
active === tab.key
|
||||
? 'text-[#006B6B] border-[#006B6B]'
|
||||
: 'text-gray-400 border-transparent hover:text-gray-600'
|
||||
: 'text-gray-500 border-transparent hover:text-gray-600'
|
||||
}`}
|
||||
>
|
||||
{tab.icon}
|
||||
<span className="hidden sm:inline">{tab.label}</span>
|
||||
<span className="sm:hidden">{tab.label.split(' ')[0]}</span>
|
||||
<span className="sm:hidden whitespace-nowrap">{tab.shortLabel}</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable react-refresh/only-export-components */
|
||||
|
||||
import { createContext, useContext, useState } from 'react'
|
||||
|
||||
const AuthContext = createContext(null)
|
||||
|
||||
@@ -8,7 +8,7 @@ function StatCard({ label, value, sub }) {
|
||||
<div className="bg-white rounded-xl shadow p-4 text-center">
|
||||
<p className="text-2xl sm:text-3xl font-bold text-[#006B6B]">{value ?? '—'}</p>
|
||||
<p className="text-xs sm:text-sm text-gray-500 mt-1">{label}</p>
|
||||
{sub && <p className="text-xs text-gray-400 mt-0.5">{sub}</p>}
|
||||
{sub && <p className="text-xs text-gray-500 mt-0.5">{sub}</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -145,6 +145,9 @@ export default function Admin() {
|
||||
const csv = [header, ...rows]
|
||||
.map((row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(';'))
|
||||
.join('\n')
|
||||
// Le BOM en tete du fichier est indispensable : sans lui, Excel ouvre le
|
||||
// CSV en ANSI et casse tous les accents. Ne pas le supprimer.
|
||||
// eslint-disable-next-line no-irregular-whitespace
|
||||
const blob = new Blob([`${csv}`], { type: 'text/csv;charset=utf-8;' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
@@ -196,7 +199,7 @@ export default function Admin() {
|
||||
|
||||
if (loading) return (
|
||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
||||
<p className="text-gray-400">Chargement...</p>
|
||||
<p className="text-gray-500">Chargement...</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -264,7 +267,7 @@ export default function Admin() {
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="text-xs text-gray-400 mt-2">
|
||||
<p className="text-xs text-gray-500 mt-2">
|
||||
Les participations durent 30 jours, puis 30 jours supplémentaires sont laissés pour retirer les lots.
|
||||
</p>
|
||||
|
||||
@@ -393,7 +396,7 @@ export default function Admin() {
|
||||
<tbody>
|
||||
{emailingContacts.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={3} className="py-6 text-center text-gray-400 text-sm">
|
||||
<td colSpan={3} className="py-6 text-center text-gray-500 text-sm">
|
||||
Aucun contact n'a accepté l'emailing pour le moment
|
||||
</td>
|
||||
</tr>
|
||||
@@ -428,7 +431,7 @@ export default function Admin() {
|
||||
onChange={(e) => setTicketCode(e.target.value.toUpperCase())}
|
||||
placeholder="Code ticket (10 caractères)"
|
||||
maxLength={10}
|
||||
className="flex-1 border border-gray-200 rounded-lg px-4 py-2.5 text-sm font-mono uppercase tracking-widest focus:outline-none focus:ring-2 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
className="flex-1 min-w-0 border border-gray-200 rounded-lg px-4 py-2.5 text-sm font-mono uppercase tracking-widest focus:outline-none focus:ring-2 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
@@ -457,7 +460,7 @@ export default function Admin() {
|
||||
<div className="px-4 py-3 flex items-start justify-between gap-3 border-b border-gray-100">
|
||||
<div>
|
||||
<p className="font-semibold text-gray-900 text-sm">{ticketResult.lot.name}</p>
|
||||
<p className="text-gray-400 text-xs mt-0.5">{ticketResult.lot.description}</p>
|
||||
<p className="text-gray-500 text-xs mt-0.5">{ticketResult.lot.description}</p>
|
||||
{ticketResult.lot.value && <p className="text-[#006B6B] text-xs font-semibold mt-1">{ticketResult.lot.value} €</p>}
|
||||
</div>
|
||||
<span className={`flex-shrink-0 text-xs font-semibold px-2.5 py-1 rounded-full ${
|
||||
@@ -527,7 +530,7 @@ export default function Admin() {
|
||||
</div>
|
||||
</div>
|
||||
{winner?.winner?.createdAt && (
|
||||
<p className="text-xs text-gray-400 text-right">
|
||||
<p className="text-xs text-gray-500 text-right">
|
||||
Tirage effectué le {formatDate(winner.winner.createdAt)}
|
||||
</p>
|
||||
)}
|
||||
@@ -572,7 +575,7 @@ export default function Admin() {
|
||||
{/* Liste participants */}
|
||||
<div className="bg-white rounded-xl shadow p-4 sm:p-6">
|
||||
<h2 className="text-base sm:text-lg font-bold text-gray-800 mb-4">
|
||||
Participants <span className="text-gray-400 font-normal text-sm">({users.length})</span>
|
||||
Participants <span className="text-gray-500 font-normal text-sm">({users.length})</span>
|
||||
</h2>
|
||||
<div className="overflow-x-auto -mx-4 sm:mx-0">
|
||||
<table className="w-full text-sm min-w-[400px]">
|
||||
@@ -586,7 +589,7 @@ export default function Admin() {
|
||||
<tbody>
|
||||
{users.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={3} className="py-6 text-center text-gray-400 text-sm">
|
||||
<td colSpan={3} className="py-6 text-center text-gray-500 text-sm">
|
||||
Aucun participant pour le moment
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -72,7 +72,7 @@ export default function Connexion() {
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Votre email"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -89,12 +89,12 @@ export default function Connexion() {
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Votre mot de passe"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 pr-12 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 pr-12 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-600"
|
||||
aria-label="Afficher/masquer le mot de passe"
|
||||
>
|
||||
<div className="w-6 h-6 rounded-full border-2 border-gray-400 flex items-center justify-center">
|
||||
@@ -105,7 +105,7 @@ export default function Connexion() {
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-right mt-1">
|
||||
<button type="button" className="text-xs text-gray-400 hover:text-[#006B6B] hover:underline">
|
||||
<button type="button" className="text-xs text-gray-500 hover:text-[#006B6B] hover:underline">
|
||||
Mot de passe oublié ?
|
||||
</button>
|
||||
</div>
|
||||
@@ -144,7 +144,7 @@ export default function Connexion() {
|
||||
type="button"
|
||||
disabled
|
||||
title="Connexion Facebook bientôt disponible"
|
||||
className="flex items-center justify-center gap-2 border border-gray-200 rounded-lg py-3 text-sm text-gray-400 cursor-not-allowed opacity-60"
|
||||
className="flex items-center justify-center gap-2 border border-gray-200 rounded-lg py-3 text-sm text-gray-500 cursor-not-allowed opacity-60"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="12" fill="#1877F2" />
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function Contact() {
|
||||
</section>
|
||||
|
||||
{/* Image */}
|
||||
<section className="rounded-2xl shadow overflow-hidden h-64 sm:h-80 md:h-117 md:self-start">
|
||||
<section className="rounded-2xl shadow overflow-hidden h-64 sm:h-80 md:h-[468px] md:self-start">
|
||||
<img
|
||||
src={imgContact}
|
||||
alt="Une personne savourant un thé"
|
||||
|
||||
@@ -84,7 +84,7 @@ export default function DefinirMotDePasse() {
|
||||
required
|
||||
minLength={8}
|
||||
placeholder="8 caractères minimum"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -100,7 +100,7 @@ export default function DefinirMotDePasse() {
|
||||
onChange={handleChange}
|
||||
required
|
||||
placeholder="Saisissez-le à nouveau"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -109,7 +109,7 @@ export default function DefinirMotDePasse() {
|
||||
type="checkbox"
|
||||
checked={showPassword}
|
||||
onChange={(e) => setShowPassword(e.target.checked)}
|
||||
className="w-4 h-4 accent-[#006B6B]"
|
||||
className="w-5 h-5 accent-[#006B6B]"
|
||||
/>
|
||||
<span className="text-xs text-gray-500">Afficher les mots de passe</span>
|
||||
</label>
|
||||
|
||||
@@ -94,7 +94,7 @@ export default function Employe() {
|
||||
required
|
||||
placeholder="Code ticket (10 caractères)"
|
||||
maxLength={10}
|
||||
className="flex-1 border border-gray-300 rounded-lg px-4 py-3 text-sm font-mono uppercase tracking-widest focus:outline-none focus:ring-2 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
className="flex-1 min-w-0 border border-gray-300 rounded-lg px-4 py-3 text-sm font-mono uppercase tracking-widest focus:outline-none focus:ring-2 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
@@ -142,7 +142,7 @@ export default function Employe() {
|
||||
|
||||
{/* Infos client */}
|
||||
<div className="px-5 py-4 space-y-2 border-b border-gray-100">
|
||||
<p className="text-xs text-gray-400 font-medium uppercase tracking-wider mb-2">Client</p>
|
||||
<p className="text-xs text-gray-500 font-medium uppercase tracking-wider mb-2">Client</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-9 h-9 rounded-full bg-[#006B6B] flex items-center justify-center text-white text-sm font-bold flex-shrink-0">
|
||||
{(result.client.firstname?.[0] ?? '?').toUpperCase()}{(result.client.lastname?.[0] ?? '?').toUpperCase()}
|
||||
@@ -158,7 +158,7 @@ export default function Employe() {
|
||||
|
||||
{/* Infos ticket */}
|
||||
<div className="px-5 py-4 space-y-1 border-b border-gray-100">
|
||||
<p className="text-xs text-gray-400 font-medium uppercase tracking-wider mb-2">Ticket</p>
|
||||
<p className="text-xs text-gray-500 font-medium uppercase tracking-wider mb-2">Ticket</p>
|
||||
<p className="text-sm text-gray-700">
|
||||
<span className="font-medium">Code :</span>{' '}
|
||||
<span className="font-mono">{result.ticket.code}</span>
|
||||
|
||||
@@ -154,7 +154,7 @@ export default function Inscription() {
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="text-center text-xs text-gray-400 mt-6">
|
||||
<p className="text-center text-xs text-gray-500 mt-6">
|
||||
Le code expire dans 15 minutes.{' '}
|
||||
<button
|
||||
type="button"
|
||||
@@ -186,19 +186,19 @@ export default function Inscription() {
|
||||
<div>
|
||||
<label htmlFor="reg-email" className="block text-sm font-medium text-gray-800 mb-1">Adresse email <span className="text-gray-800">*</span></label>
|
||||
<input type="email" id="reg-email" name="email" value={form.email} onChange={handleChange} required placeholder="Votre email"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="reg-lastName" className="block text-sm font-medium text-gray-800 mb-1">Nom <span className="text-gray-800">*</span></label>
|
||||
<input type="text" id="reg-lastName" name="lastName" value={form.lastName} onChange={handleChange} required placeholder="Votre nom"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="reg-firstName" className="block text-sm font-medium text-gray-800 mb-1">Prénom <span className="text-gray-800">*</span></label>
|
||||
<input type="text" id="reg-firstName" name="firstName" value={form.firstName} onChange={handleChange} required placeholder="Votre prénom"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -222,9 +222,9 @@ export default function Inscription() {
|
||||
<label htmlFor="reg-password" className="block text-sm font-medium text-gray-800 mb-1">Mot de passe <span className="text-gray-800">*</span></label>
|
||||
<div className="relative">
|
||||
<input type={showPassword ? 'text' : 'password'} id="reg-password" name="password" value={form.password} onChange={handleChange} required placeholder="Votre mot de passe"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 pr-12 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 pr-12 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]" />
|
||||
<button type="button" onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600" aria-label="Afficher/masquer">
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-600" aria-label="Afficher/masquer">
|
||||
<div className="w-6 h-6 rounded-full border-2 border-gray-400 flex items-center justify-center">
|
||||
{showPassword && <div className="w-1.5 h-1.5 rounded-full bg-gray-400" />}
|
||||
</div>
|
||||
@@ -234,7 +234,7 @@ export default function Inscription() {
|
||||
|
||||
<label className="flex items-start gap-3 cursor-pointer">
|
||||
<input type="checkbox" checked={acceptedTerms} onChange={(e) => setAcceptedTerms(e.target.checked)} required
|
||||
className="mt-0.5 w-4 h-4 accent-[#006B6B] flex-shrink-0" />
|
||||
className="mt-0.5 w-5 h-5 accent-[#006B6B] flex-shrink-0" />
|
||||
<span className="text-xs text-gray-500 leading-relaxed">
|
||||
J'accepte les{' '}
|
||||
<Link to="/cgu" className="text-[#006B6B] hover:underline font-medium">conditions d'utilisation</Link>{' '}
|
||||
@@ -246,10 +246,10 @@ export default function Inscription() {
|
||||
<label className="flex items-start gap-3 cursor-pointer">
|
||||
<input type="checkbox" name="marketingOptIn" checked={marketingOptIn}
|
||||
onChange={(e) => setMarketingOptIn(e.target.checked)}
|
||||
className="mt-0.5 w-4 h-4 accent-[#006B6B] flex-shrink-0" />
|
||||
className="mt-0.5 w-5 h-5 accent-[#006B6B] flex-shrink-0" />
|
||||
<span className="text-xs text-gray-500 leading-relaxed">
|
||||
J'accepte de recevoir les offres et actualités de Thé Tip Top par email.
|
||||
<span className="block text-gray-400 mt-0.5">
|
||||
<span className="block text-gray-500 mt-0.5">
|
||||
Facultatif — vous pouvez vous désinscrire à tout moment.
|
||||
</span>
|
||||
</span>
|
||||
@@ -277,7 +277,7 @@ export default function Inscription() {
|
||||
</svg>
|
||||
Google
|
||||
</button>
|
||||
<button type="button" disabled title="Inscription Facebook bientôt disponible" className="flex items-center justify-center gap-2 border border-gray-200 rounded-lg py-3 text-sm text-gray-400 cursor-not-allowed opacity-60">
|
||||
<button type="button" disabled title="Inscription Facebook bientôt disponible" className="flex items-center justify-center gap-2 border border-gray-200 rounded-lg py-3 text-sm text-gray-500 cursor-not-allowed opacity-60">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="12" fill="#1877F2" />
|
||||
<path fill="#FFFFFF" d="M13.5 21v-8h2.7l.4-3h-3.1V8.1c0-.9.3-1.6 1.7-1.6h1.8V3.8c-.3 0-1.4-.1-2.6-.1-2.6 0-4.4 1.6-4.4 4.5V10H7.2v3H10v8h3.5z" />
|
||||
|
||||
@@ -70,7 +70,7 @@ export default function MesGains() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{loading && <p className="text-center text-gray-400 text-sm mt-8">Chargement…</p>}
|
||||
{loading && <p className="text-center text-gray-500 text-sm mt-8">Chargement…</p>}
|
||||
|
||||
{error && (
|
||||
<div className="px-4 py-3 rounded-lg bg-red-50 border border-red-200 text-red-600 text-sm">
|
||||
@@ -80,8 +80,8 @@ export default function MesGains() {
|
||||
|
||||
{!loading && !error && gains.length === 0 && (
|
||||
<div className="text-center mt-12">
|
||||
<p className="text-gray-400 text-sm">Aucun gain pour le moment.</p>
|
||||
<p className="text-gray-400 text-xs mt-1">Saisissez un code pour participer !</p>
|
||||
<p className="text-gray-500 text-sm">Aucun gain pour le moment.</p>
|
||||
<p className="text-gray-500 text-xs mt-1">Saisissez un code pour participer !</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -105,7 +105,7 @@ export default function MesGains() {
|
||||
</div>
|
||||
<p className="text-gray-500 text-xs mt-1 leading-relaxed">{gain.description}</p>
|
||||
{gain.value && (
|
||||
<p className="text-gray-400 text-xs mt-0.5">Valeur : {gain.value} €</p>
|
||||
<p className="text-gray-500 text-xs mt-0.5">Valeur : {gain.value} €</p>
|
||||
)}
|
||||
<div className="flex items-center gap-2 mt-2 flex-wrap">
|
||||
<p className="text-[#006B6B] text-xs">Gagné le {formatDate(gain.wonAt)}</p>
|
||||
@@ -115,7 +115,7 @@ export default function MesGains() {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-gray-400 text-[10px] mt-1">
|
||||
<p className="text-gray-500 text-[10px] mt-1">
|
||||
Code utilisé :{' '}
|
||||
<span className="font-mono tracking-widest text-gray-600">{gain.ticketCode}</span>
|
||||
</p>
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function NotFound() {
|
||||
Page introuvable
|
||||
</h1>
|
||||
|
||||
<p className="text-gray-400 text-sm sm:text-base max-w-xs mb-8">
|
||||
<p className="text-gray-500 text-sm sm:text-base max-w-xs mb-8">
|
||||
La page que vous recherchez n'existe pas ou a été déplacée.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ export default function Perdu() {
|
||||
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center px-8 max-w-sm mx-auto text-center">
|
||||
|
||||
<div className="w-20 h-20 bg-gray-200 rounded-full flex items-center justify-center mb-6">
|
||||
<svg className="w-10 h-10 text-gray-400" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
|
||||
<svg className="w-10 h-10 text-gray-500" fill="none" stroke="currentColor" strokeWidth={2} viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
@@ -22,7 +22,7 @@ export default function Perdu() {
|
||||
{message || 'Ce code est invalide ou a déjà été utilisé.'}
|
||||
</p>
|
||||
|
||||
<p className="text-gray-400 text-xs mb-8">
|
||||
<p className="text-gray-500 text-xs mb-8">
|
||||
Vérifiez le code présent sur votre ticket et réessayez.
|
||||
</p>
|
||||
|
||||
|
||||
@@ -102,19 +102,19 @@ export default function Profile() {
|
||||
{/* Infos du compte (lecture seule) */}
|
||||
<div className="space-y-4 mb-8">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-400 uppercase tracking-wider mb-1">Adresse email</label>
|
||||
<div className="w-full border border-gray-100 rounded-lg px-4 py-3 text-sm text-gray-400 bg-gray-50 cursor-not-allowed select-none">
|
||||
<label className="block text-xs font-medium text-gray-500 uppercase tracking-wider mb-1">Adresse email</label>
|
||||
<div className="w-full border border-gray-100 rounded-lg px-4 py-3 text-sm text-gray-500 bg-gray-50 cursor-not-allowed select-none">
|
||||
{user.email || '—'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-400 uppercase tracking-wider mb-1">Nom</label>
|
||||
<label className="block text-xs font-medium text-gray-500 uppercase tracking-wider mb-1">Nom</label>
|
||||
<div className="w-full border border-gray-100 rounded-lg px-4 py-3 text-sm text-gray-500 bg-gray-50">
|
||||
{user.lastname || '—'}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-400 uppercase tracking-wider mb-1">Prénom</label>
|
||||
<label className="block text-xs font-medium text-gray-500 uppercase tracking-wider mb-1">Prénom</label>
|
||||
<div className="w-full border border-gray-100 rounded-lg px-4 py-3 text-sm text-gray-500 bg-gray-50">
|
||||
{user.firstname || '—'}
|
||||
</div>
|
||||
@@ -189,12 +189,12 @@ export default function Profile() {
|
||||
onChange={handlePwChange}
|
||||
required
|
||||
placeholder="••••••••"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 pr-11 text-sm text-gray-700 placeholder-gray-300 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 pr-11 text-sm text-gray-700 placeholder-gray-500 focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPw((s) => ({ ...s, [key]: !s[key] }))}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600"
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-600"
|
||||
aria-label="Afficher/masquer"
|
||||
>
|
||||
<EyeIcon visible={showPw[key]} />
|
||||
|
||||
@@ -17,7 +17,6 @@ export default function SaisirCodeUnique() {
|
||||
const [code, setCode] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [gain, setGain] = useState(null)
|
||||
const [claim, setClaim] = useState(null)
|
||||
const [wonAt, setWonAt] = useState(null)
|
||||
const [showDetail, setShowDetail] = useState(false)
|
||||
|
||||
@@ -27,7 +26,6 @@ export default function SaisirCodeUnique() {
|
||||
try {
|
||||
const data = await participateApi(code.trim().toUpperCase())
|
||||
setGain(data.gain)
|
||||
setClaim(data.claim)
|
||||
setWonAt(new Date())
|
||||
} catch (err) {
|
||||
if (!handleAuthError(err)) {
|
||||
@@ -40,7 +38,6 @@ export default function SaisirCodeUnique() {
|
||||
|
||||
const handleClose = () => {
|
||||
setGain(null)
|
||||
setClaim(null)
|
||||
setWonAt(null)
|
||||
setShowDetail(false)
|
||||
setCode('')
|
||||
@@ -65,7 +62,7 @@ export default function SaisirCodeUnique() {
|
||||
placeholder="Votre code"
|
||||
maxLength={10}
|
||||
required
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-300 bg-white focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
className="w-full border border-gray-200 rounded-lg px-4 py-3 text-sm text-gray-700 placeholder-gray-500 bg-white focus:outline-none focus:ring-1 focus:ring-[#006B6B] focus:border-[#006B6B]"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
@@ -147,28 +144,28 @@ export default function SaisirCodeUnique() {
|
||||
{/* Valeur */}
|
||||
{gain.value && (
|
||||
<div className="flex items-center justify-between py-2 border-b border-gray-100">
|
||||
<span className="text-xs text-gray-400 uppercase tracking-wider">Valeur estimée</span>
|
||||
<span className="text-xs text-gray-500 uppercase tracking-wider">Valeur estimée</span>
|
||||
<span className="text-sm font-semibold text-[#006B6B]">{gain.value} €</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Code utilisé */}
|
||||
<div className="flex items-center justify-between py-2 border-b border-gray-100">
|
||||
<span className="text-xs text-gray-400 uppercase tracking-wider">Code utilisé</span>
|
||||
<span className="text-xs text-gray-500 uppercase tracking-wider">Code utilisé</span>
|
||||
<span className="font-mono text-sm tracking-widest text-gray-700">{code}</span>
|
||||
</div>
|
||||
|
||||
{/* Date & heure */}
|
||||
{wonAt && (
|
||||
<div className="flex items-center justify-between py-2 border-b border-gray-100">
|
||||
<span className="text-xs text-gray-400 uppercase tracking-wider">Obtenu le</span>
|
||||
<span className="text-xs text-gray-500 uppercase tracking-wider">Obtenu le</span>
|
||||
<span className="text-sm text-gray-700">{formatDateTime(wonAt)}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Statut */}
|
||||
<div className="flex items-center justify-between py-2">
|
||||
<span className="text-xs text-gray-400 uppercase tracking-wider">Statut</span>
|
||||
<span className="text-xs text-gray-500 uppercase tracking-wider">Statut</span>
|
||||
<span className="text-xs font-semibold px-2.5 py-1 rounded-full bg-yellow-100 text-yellow-700">
|
||||
En attente de remise
|
||||
</span>
|
||||
|
||||
@@ -88,7 +88,7 @@ export default function Sitemap() {
|
||||
<p className="text-sm font-medium text-gray-800 group-hover:text-[#006B6B] transition-colors">
|
||||
{link.label}
|
||||
</p>
|
||||
<p className="text-xs text-gray-400 mt-0.5">{link.desc}</p>
|
||||
<p className="text-xs text-gray-500 mt-0.5">{link.desc}</p>
|
||||
</div>
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user