feat: modern UI redesign with SVG icons and improved styling
- Update client dashboard with modern cards, SVG statistics icons, and prize icons - Add roulette animation with colored prize icons during ticket draw - Redesign history page with 4 statistics cards and SVG icons - Add "Rejetés" filter button in history page - Update profile page with modern card styling - Redesign header with clickable user name/email button - Add Facebook login button with green border styling - Update game page with roulette animation and prize display - Add prize values to constants (15€, 25€, 39€, 69€) - Replace all emoji icons with professional SVG icons - Apply consistent color scheme: green (#1a4d2e, #2d5a3d) and orange (#f59e0b) - Improve button styles and hover effects across all pages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f822077f51
commit
70f61fca88
|
|
@ -78,7 +78,8 @@ export default function ClientPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8">
|
<div className="min-h-screen bg-gray-50 py-8">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
{/* Welcome Section */}
|
{/* Welcome Section */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
||||||
|
|
@ -91,94 +92,95 @@ export default function ClientPage() {
|
||||||
|
|
||||||
{/* Quick Action */}
|
{/* Quick Action */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<Card className="bg-gradient-to-r from-primary-500 to-primary-600 text-white">
|
<div className="bg-gradient-to-r from-[#1a4d2e] to-[#2d5a3d] text-white rounded-xl shadow-md p-8">
|
||||||
<CardContent className="py-8">
|
|
||||||
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold mb-2">
|
<h2 className="text-2xl font-bold mb-2">
|
||||||
Vous avez un nouveau ticket ?
|
Vous avez un nouveau ticket ?
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-primary-50">
|
<p className="text-green-50">
|
||||||
Entrez votre code et découvrez votre gain instantanément
|
Entrez votre code et découvrez votre gain instantanément
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Link href={ROUTES.GAME}>
|
<Link href={ROUTES.GAME}>
|
||||||
<Button
|
<button className="bg-[#f59e0b] hover:bg-[#d97706] text-white font-bold px-8 py-4 rounded-lg transition-all hover:shadow-xl whitespace-nowrap">
|
||||||
size="lg"
|
|
||||||
className="bg-white text-black hover:bg-gray-50 border-2 border-black"
|
|
||||||
>
|
|
||||||
Jouer maintenant 🎮
|
Jouer maintenant 🎮
|
||||||
</Button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Statistics Cards */}
|
{/* Statistics Cards */}
|
||||||
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
||||||
<Card>
|
<div className="bg-white rounded-xl shadow-md p-6">
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600">
|
<p className="text-sm font-medium text-gray-600 mb-2">
|
||||||
Total Participations
|
Total Participations
|
||||||
</p>
|
</p>
|
||||||
<p className="text-3xl font-bold text-gray-900 mt-2">
|
<p className="text-4xl font-bold text-gray-900">
|
||||||
{stats.total}
|
{stats.total}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-4xl">🎫</div>
|
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center">
|
||||||
|
<svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
<div className="bg-white rounded-xl shadow-md p-6">
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600">
|
<p className="text-sm font-medium text-gray-600 mb-2">
|
||||||
Gains réclamés
|
Gains réclamés
|
||||||
</p>
|
</p>
|
||||||
<p className="text-3xl font-bold text-green-600 mt-2">
|
<p className="text-4xl font-bold text-green-600">
|
||||||
{stats.claimed}
|
{stats.claimed}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-4xl">✅</div>
|
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center">
|
||||||
|
<svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
<div className="bg-white rounded-xl shadow-md p-6">
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600">
|
<p className="text-sm font-medium text-gray-600 mb-2">
|
||||||
En attente
|
En attente
|
||||||
</p>
|
</p>
|
||||||
<p className="text-3xl font-bold text-yellow-600 mt-2">
|
<p className="text-4xl font-bold text-yellow-600">
|
||||||
{stats.pending}
|
{stats.pending}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-4xl">⏳</div>
|
<div className="w-16 h-16 bg-yellow-100 rounded-full flex items-center justify-center">
|
||||||
|
<svg className="w-8 h-8 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Recent Tickets */}
|
{/* Recent Tickets */}
|
||||||
<Card>
|
<div className="bg-white rounded-xl shadow-md overflow-hidden">
|
||||||
<CardHeader>
|
<div className="px-6 py-4 border-b border-gray-200">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<CardTitle>Mes derniers tickets</CardTitle>
|
<h2 className="text-xl font-bold text-gray-900">Mes derniers tickets</h2>
|
||||||
<Link href={ROUTES.HISTORY}>
|
<Link href={ROUTES.HISTORY}>
|
||||||
<Button variant="outline" size="sm">
|
<button className="text-[#1a4d2e] hover:text-[#f59e0b] font-semibold text-sm transition-colors">
|
||||||
Voir tout l'historique
|
Voir tout l'historique
|
||||||
</Button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent>
|
<div className="p-6">
|
||||||
{tickets.length === 0 ? (
|
{tickets.length === 0 ? (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<div className="text-6xl mb-4">🎲</div>
|
<div className="text-6xl mb-4">🎲</div>
|
||||||
|
|
@ -186,51 +188,84 @@ export default function ClientPage() {
|
||||||
Vous n'avez pas encore participé au jeu
|
Vous n'avez pas encore participé au jeu
|
||||||
</p>
|
</p>
|
||||||
<Link href={ROUTES.GAME}>
|
<Link href={ROUTES.GAME}>
|
||||||
<Button className="bg-white text-black hover:bg-gray-50 border-2 border-black">Jouer maintenant</Button>
|
<button className="bg-[#f59e0b] hover:bg-[#d97706] text-white font-bold px-6 py-3 rounded-lg transition-all">
|
||||||
|
Jouer maintenant
|
||||||
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full">
|
||||||
<thead className="bg-gray-50">
|
<thead>
|
||||||
<tr>
|
<tr className="border-b border-gray-200">
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Code Ticket
|
Code Ticket
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Gain
|
Gain
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Statut
|
Statut
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Date
|
Date
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="divide-y divide-gray-200">
|
||||||
{tickets.slice(0, 5).map((ticket) => {
|
{tickets.slice(0, 5).map((ticket) => {
|
||||||
const prizeConfig = ticket.prize
|
const prizeConfig = ticket.prize
|
||||||
? PRIZE_CONFIG[ticket.prize.type as keyof typeof PRIZE_CONFIG]
|
? PRIZE_CONFIG[ticket.prize.type as keyof typeof PRIZE_CONFIG]
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={ticket.id} className="hover:bg-gray-50">
|
<tr key={ticket.id} className="hover:bg-gray-50 transition-colors">
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
<span className="font-mono text-sm font-medium text-gray-900">
|
<span className="font-mono text-sm font-semibold text-gray-900">
|
||||||
{ticket.code}
|
{ticket.code}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center gap-3">
|
||||||
{prizeConfig && (
|
{prizeConfig && (
|
||||||
<>
|
<>
|
||||||
<span className="text-2xl mr-2">
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${prizeConfig.color}`}>
|
||||||
{prizeConfig.icon}
|
{ticket.prize?.type === 'INFUSEUR' && (
|
||||||
</span>
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
<span className="text-sm text-gray-900">
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{ticket.prize?.type === 'THE_SIGNATURE' && (
|
||||||
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{ticket.prize?.type === 'COFFRET_DECOUVERTE' && (
|
||||||
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{ticket.prize?.type === 'COFFRET_PRESTIGE' && (
|
||||||
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 2L4 5v6.09c0 5.05 3.41 9.76 8 10.91 4.59-1.15 8-5.86 8-10.91V5l-8-3zm6 9.09c0 4-2.55 7.7-6 8.83-3.45-1.13-6-4.82-6-8.83v-4.7l6-2.25 6 2.25v4.7zM8 10.5l1.5 1.5L15 6.5 13.5 5z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{ticket.prize?.type === 'THE_GRATUIT' && (
|
||||||
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-900">
|
||||||
{prizeConfig.name}
|
{prizeConfig.name}
|
||||||
</span>
|
</p>
|
||||||
|
{ticket.prize?.value && ticket.prize.value > 0 && (
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
{ticket.prize.value}€
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -238,7 +273,7 @@ export default function ClientPage() {
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
{getStatusBadge(ticket.status)}
|
{getStatusBadge(ticket.status)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
|
||||||
{ticket.playedAt ? new Date(ticket.playedAt).toLocaleDateString("fr-FR") : "-"}
|
{ticket.playedAt ? new Date(ticket.playedAt).toLocaleDateString("fr-FR") : "-"}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
@ -248,8 +283,9 @@ export default function ClientPage() {
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,10 +103,11 @@ export default function HistoriquePage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8">
|
<div className="min-h-screen bg-gray-50 py-8">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h1 className="text-4xl font-bold text-gray-900 mb-2 flex items-center gap-3">
|
<h1 className="text-4xl font-bold text-gray-900 mb-2 flex items-center gap-3">
|
||||||
<Calendar className="w-10 h-10 text-primary-600" />
|
<Calendar className="w-10 h-10 text-[#1a4d2e]" />
|
||||||
Historique de mes participations
|
Historique de mes participations
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
|
|
@ -115,56 +116,64 @@ export default function HistoriquePage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-4 gap-6 mb-8">
|
<div className="grid md:grid-cols-4 gap-6 mb-8">
|
||||||
<Card className="bg-gradient-to-br from-blue-50 to-blue-100">
|
<div className="bg-white rounded-xl shadow-md p-6">
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-600">Total</p>
|
<p className="text-sm font-medium text-gray-600 mb-2">Total</p>
|
||||||
<p className="text-3xl font-bold text-blue-600 mt-2">{stats.total}</p>
|
<p className="text-4xl font-bold text-blue-600">{stats.total}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-4xl">📊</div>
|
<div className="w-16 h-16 bg-blue-100 rounded-full flex items-center justify-center">
|
||||||
|
<svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
<Card className="bg-gradient-to-br from-green-50 to-green-100">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-600">Réclamés</p>
|
|
||||||
<p className="text-3xl font-bold text-green-600 mt-2">{stats.claimed}</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-4xl">✅</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="bg-gradient-to-br from-yellow-50 to-yellow-100">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-600">En attente</p>
|
|
||||||
<p className="text-3xl font-bold text-yellow-600 mt-2">{stats.pending}</p>
|
|
||||||
</div>
|
|
||||||
<div className="text-4xl">⏳</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="bg-gradient-to-br from-red-50 to-red-100">
|
|
||||||
<CardContent className="pt-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<p className="text-sm font-medium text-gray-600">Rejetés</p>
|
|
||||||
<p className="text-3xl font-bold text-red-600 mt-2">{stats.rejected}</p>
|
|
||||||
</div>
|
|
||||||
<div className="text-4xl">❌</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card className="mb-6">
|
<div className="bg-white rounded-xl shadow-md p-6">
|
||||||
<CardContent className="pt-6">
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-600 mb-2">Réclamés</p>
|
||||||
|
<p className="text-4xl font-bold text-green-600">{stats.claimed}</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center">
|
||||||
|
<svg className="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-xl shadow-md p-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-600 mb-2">En attente</p>
|
||||||
|
<p className="text-4xl font-bold text-yellow-600">{stats.pending}</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-16 h-16 bg-yellow-100 rounded-full flex items-center justify-center">
|
||||||
|
<svg className="w-8 h-8 text-yellow-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-xl shadow-md p-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-medium text-gray-600 mb-2">Rejetés</p>
|
||||||
|
<p className="text-4xl font-bold text-red-600">{stats.rejected}</p>
|
||||||
|
</div>
|
||||||
|
<div className="w-16 h-16 bg-red-100 rounded-full flex items-center justify-center">
|
||||||
|
<svg className="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-white rounded-xl shadow-md p-6 mb-6">
|
||||||
<div className="flex flex-col md:flex-row gap-4">
|
<div className="flex flex-col md:flex-row gap-4">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
|
@ -174,7 +183,7 @@ export default function HistoriquePage() {
|
||||||
placeholder="Rechercher par code ticket..."
|
placeholder="Rechercher par code ticket..."
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent"
|
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-[#1a4d2e] focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -182,9 +191,9 @@ export default function HistoriquePage() {
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilter('ALL')}
|
onClick={() => setFilter('ALL')}
|
||||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
className={`px-4 py-2 rounded-lg font-semibold transition-all ${
|
||||||
filter === 'ALL'
|
filter === 'ALL'
|
||||||
? 'bg-primary-600 text-white'
|
? 'bg-[#1a4d2e] text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
@ -192,7 +201,7 @@ export default function HistoriquePage() {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilter('CLAIMED')}
|
onClick={() => setFilter('CLAIMED')}
|
||||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
className={`px-4 py-2 rounded-lg font-semibold transition-all ${
|
||||||
filter === 'CLAIMED'
|
filter === 'CLAIMED'
|
||||||
? 'bg-green-600 text-white'
|
? 'bg-green-600 text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
|
|
@ -202,7 +211,7 @@ export default function HistoriquePage() {
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setFilter('PENDING')}
|
onClick={() => setFilter('PENDING')}
|
||||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
className={`px-4 py-2 rounded-lg font-semibold transition-all ${
|
||||||
filter === 'PENDING'
|
filter === 'PENDING'
|
||||||
? 'bg-yellow-600 text-white'
|
? 'bg-yellow-600 text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
|
|
@ -210,16 +219,25 @@ export default function HistoriquePage() {
|
||||||
>
|
>
|
||||||
En attente ({stats.pending})
|
En attente ({stats.pending})
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setFilter('REJECTED')}
|
||||||
|
className={`px-4 py-2 rounded-lg font-semibold transition-all ${
|
||||||
|
filter === 'REJECTED'
|
||||||
|
? 'bg-red-600 text-white'
|
||||||
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Rejetés ({stats.rejected})
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
<div className="bg-white rounded-xl shadow-md overflow-hidden">
|
||||||
<CardHeader>
|
<div className="px-6 py-4 border-b border-gray-200">
|
||||||
<CardTitle>Tous mes tickets ({filteredTickets.length})</CardTitle>
|
<h2 className="text-xl font-bold text-gray-900">Tous mes tickets ({filteredTickets.length})</h2>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent>
|
<div className="p-6">
|
||||||
{filteredTickets.length === 0 ? (
|
{filteredTickets.length === 0 ? (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<div className="text-6xl mb-4">🎲</div>
|
<div className="text-6xl mb-4">🎲</div>
|
||||||
|
|
@ -229,60 +247,84 @@ export default function HistoriquePage() {
|
||||||
: 'Vous n\'avez pas encore participé au jeu'}
|
: 'Vous n\'avez pas encore participé au jeu'}
|
||||||
</p>
|
</p>
|
||||||
{!searchQuery && filter === 'ALL' && (
|
{!searchQuery && filter === 'ALL' && (
|
||||||
<Button onClick={() => router.push(ROUTES.GAME)} className="bg-white text-black hover:bg-gray-50 border-2 border-black">
|
<button
|
||||||
|
onClick={() => router.push(ROUTES.GAME)}
|
||||||
|
className="bg-[#f59e0b] hover:bg-[#d97706] text-white font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
|
>
|
||||||
Jouer maintenant
|
Jouer maintenant
|
||||||
</Button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="min-w-full divide-y divide-gray-200">
|
<table className="min-w-full">
|
||||||
<thead className="bg-gray-50">
|
<thead>
|
||||||
<tr>
|
<tr className="border-b border-gray-200">
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Code Ticket
|
Code Ticket
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Gain
|
Gain
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Statut
|
Statut
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Date de participation
|
Date de participation
|
||||||
</th>
|
</th>
|
||||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
<th className="px-6 py-3 text-left text-xs font-semibold text-gray-700 uppercase tracking-wider">
|
||||||
Date de réclamation
|
Date de réclamation
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody className="bg-white divide-y divide-gray-200">
|
<tbody className="divide-y divide-gray-200">
|
||||||
{filteredTickets.map((ticket) => {
|
{filteredTickets.map((ticket) => {
|
||||||
const prizeConfig = ticket.prize
|
const prizeConfig = ticket.prize
|
||||||
? PRIZE_CONFIG[ticket.prize.type as keyof typeof PRIZE_CONFIG]
|
? PRIZE_CONFIG[ticket.prize.type as keyof typeof PRIZE_CONFIG]
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr key={ticket.id} className="hover:bg-gray-50">
|
<tr key={ticket.id} className="hover:bg-gray-50 transition-colors">
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
<span className="font-mono text-sm font-medium text-gray-900">
|
<span className="font-mono text-sm font-semibold text-gray-900">
|
||||||
{ticket.code}
|
{ticket.code}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center gap-3">
|
||||||
{prizeConfig && (
|
{prizeConfig && (
|
||||||
<>
|
<>
|
||||||
<span className="text-2xl mr-2">
|
<div className={`w-10 h-10 rounded-full flex items-center justify-center ${prizeConfig.color}`}>
|
||||||
{prizeConfig.icon}
|
{ticket.prize?.type === 'INFUSEUR' && (
|
||||||
</span>
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{ticket.prize?.type === 'THE_SIGNATURE' && (
|
||||||
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{ticket.prize?.type === 'COFFRET_DECOUVERTE' && (
|
||||||
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{ticket.prize?.type === 'COFFRET_PRESTIGE' && (
|
||||||
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 2L4 5v6.09c0 5.05 3.41 9.76 8 10.91 4.59-1.15 8-5.86 8-10.91V5l-8-3zm6 9.09c0 4-2.55 7.7-6 8.83-3.45-1.13-6-4.82-6-8.83v-4.7l6-2.25 6 2.25v4.7zM8 10.5l1.5 1.5L15 6.5 13.5 5z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{ticket.prize?.type === 'THE_GRATUIT' && (
|
||||||
|
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium text-gray-900">
|
<p className="text-sm font-medium text-gray-900">
|
||||||
{prizeConfig.name}
|
{prizeConfig.name}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-500">
|
|
||||||
{ticket.prize?.value}€
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
@ -291,7 +333,7 @@ export default function HistoriquePage() {
|
||||||
<td className="px-6 py-4 whitespace-nowrap">
|
<td className="px-6 py-4 whitespace-nowrap">
|
||||||
{getStatusBadge(ticket.status)}
|
{getStatusBadge(ticket.status)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
|
||||||
{ticket.playedAt
|
{ticket.playedAt
|
||||||
? new Date(ticket.playedAt).toLocaleDateString("fr-FR", {
|
? new Date(ticket.playedAt).toLocaleDateString("fr-FR", {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
|
|
@ -302,7 +344,7 @@ export default function HistoriquePage() {
|
||||||
})
|
})
|
||||||
: "-"}
|
: "-"}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
|
||||||
{ticket.claimedAt
|
{ticket.claimedAt
|
||||||
? new Date(ticket.claimedAt).toLocaleDateString("fr-FR", {
|
? new Date(ticket.claimedAt).toLocaleDateString("fr-FR", {
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
|
|
@ -320,8 +362,9 @@ export default function HistoriquePage() {
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
"use client";
|
"use client";
|
||||||
import { useState } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
import { useAuth } from "@/contexts/AuthContext";
|
||||||
|
|
@ -15,13 +15,24 @@ import { useRouter } from "next/navigation";
|
||||||
import { ROUTES } from "@/utils/constants";
|
import { ROUTES } from "@/utils/constants";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
const PRIZES = [
|
||||||
|
{ type: 'INFUSEUR', name: 'Infuseur à thé', color: 'bg-blue-100 text-blue-800' },
|
||||||
|
{ type: 'THE_SIGNATURE', name: 'Thé signature 100g', color: 'bg-green-100 text-green-800' },
|
||||||
|
{ type: 'COFFRET_DECOUVERTE', name: 'Coffret découverte 39€', color: 'bg-purple-100 text-purple-800' },
|
||||||
|
{ type: 'COFFRET_PRESTIGE', name: 'Coffret prestige 69€', color: 'bg-amber-100 text-amber-800' },
|
||||||
|
{ type: 'THE_GRATUIT', name: 'Thé gratuit en magasin', color: 'bg-pink-100 text-pink-800' },
|
||||||
|
];
|
||||||
|
|
||||||
export default function JeuxPage() {
|
export default function JeuxPage() {
|
||||||
const { user, isAuthenticated } = useAuth();
|
const { user, isAuthenticated } = useAuth();
|
||||||
const { play, isPlaying } = useGame();
|
const { play, isPlaying } = useGame();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [showResultModal, setShowResultModal] = useState(false);
|
const [showResultModal, setShowResultModal] = useState(false);
|
||||||
|
const [showRouletteModal, setShowRouletteModal] = useState(false);
|
||||||
const [gameResult, setGameResult] = useState<PlayGameResponse | null>(null);
|
const [gameResult, setGameResult] = useState<PlayGameResponse | null>(null);
|
||||||
const [errorMessage, setErrorMessage] = useState<string>("");
|
const [errorMessage, setErrorMessage] = useState<string>("");
|
||||||
|
const [currentPrizeIndex, setCurrentPrizeIndex] = useState(0);
|
||||||
|
const [isSpinning, setIsSpinning] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
|
|
@ -32,6 +43,17 @@ export default function JeuxPage() {
|
||||||
resolver: zodResolver(ticketCodeSchema),
|
resolver: zodResolver(ticketCodeSchema),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Animation de la roulette
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSpinning) {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setCurrentPrizeIndex((prev) => (prev + 1) % PRIZES.length);
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, [isSpinning]);
|
||||||
|
|
||||||
const onSubmit = async (data: TicketCodeFormData) => {
|
const onSubmit = async (data: TicketCodeFormData) => {
|
||||||
// Réinitialiser le message d'erreur
|
// Réinitialiser le message d'erreur
|
||||||
setErrorMessage("");
|
setErrorMessage("");
|
||||||
|
|
@ -42,14 +64,37 @@ export default function JeuxPage() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Afficher la modal de roulette et démarrer l'animation
|
||||||
|
setShowRouletteModal(true);
|
||||||
|
setIsSpinning(true);
|
||||||
|
setCurrentPrizeIndex(0);
|
||||||
|
|
||||||
const result = await play(data.ticketCode);
|
const result = await play(data.ticketCode);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
// Trouver l'index du prix gagné
|
||||||
|
const winningIndex = PRIZES.findIndex(p => p.type === result.prize?.type);
|
||||||
|
|
||||||
|
// Continuer à tourner pendant 3 secondes
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsSpinning(false);
|
||||||
|
if (winningIndex !== -1) {
|
||||||
|
setCurrentPrizeIndex(winningIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Afficher le résultat après 2 secondes (pour montrer le gain)
|
||||||
|
setTimeout(() => {
|
||||||
|
setShowRouletteModal(false);
|
||||||
setGameResult(result);
|
setGameResult(result);
|
||||||
setShowResultModal(true);
|
setShowResultModal(true);
|
||||||
setErrorMessage("");
|
setErrorMessage("");
|
||||||
reset();
|
reset();
|
||||||
|
}, 2000);
|
||||||
|
}, 3000);
|
||||||
} else {
|
} else {
|
||||||
// En cas d'erreur, afficher un message personnalisé
|
// En cas d'erreur, fermer la roulette et afficher l'erreur
|
||||||
|
setIsSpinning(false);
|
||||||
|
setShowRouletteModal(false);
|
||||||
setErrorMessage("Ce code a déjà été utilisé ou est invalide. Si vous avez déjà utilisé ce code, consultez vos tickets dans 'Mes lots'.");
|
setErrorMessage("Ce code a déjà été utilisé ou est invalide. Si vous avez déjà utilisé ce code, consultez vos tickets dans 'Mes lots'.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -64,28 +109,29 @@ export default function JeuxPage() {
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8">
|
<div className="min-h-screen bg-gray-50 py-8">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
{/* Formulaire Section */}
|
{/* Formulaire Section */}
|
||||||
<section className="mb-16">
|
<section className="mb-16">
|
||||||
<div className="max-w-2xl mx-auto">
|
<div className="max-w-2xl mx-auto">
|
||||||
<Card className="shadow-xl">
|
<div className="bg-white rounded-xl shadow-md overflow-hidden">
|
||||||
<CardHeader className="bg-gradient-to-r from-primary-50 to-green-50">
|
<div className="bg-gradient-to-r from-[#1a4d2e] to-[#2d5a3d] px-6 py-6">
|
||||||
<CardTitle className="text-center text-2xl md:text-3xl text-primary-800">
|
<h1 className="text-center text-3xl md:text-4xl font-bold text-white">
|
||||||
🎁 Jouez maintenant !
|
🎁 Jouez maintenant !
|
||||||
</CardTitle>
|
</h1>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent className="pt-8">
|
<div className="p-8">
|
||||||
<div className="mb-6 text-center">
|
<div className="mb-6 text-center">
|
||||||
{isAuthenticated ? (
|
{isAuthenticated ? (
|
||||||
<p className="text-gray-700">
|
<p className="text-gray-700 text-lg">
|
||||||
Bonjour <span className="font-bold text-primary-600">{user?.firstName}</span>,
|
Bonjour <span className="font-bold text-[#1a4d2e]">{user?.firstName}</span>,
|
||||||
entrez le code de 10 caractères présent sur votre ticket de caisse
|
entrez le code de 10 caractères présent sur votre ticket de caisse
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-4">
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-4 mb-4">
|
||||||
<p className="text-yellow-800 text-sm">
|
<p className="text-yellow-800 text-sm">
|
||||||
💡 Vous devez être connecté pour valider votre code.
|
💡 Vous devez être connecté pour valider votre code.
|
||||||
<Link href={ROUTES.LOGIN} className="font-semibold underline ml-1">
|
<Link href={ROUTES.LOGIN} className="font-semibold underline ml-1 hover:text-yellow-900">
|
||||||
Connectez-vous
|
Connectez-vous
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
|
|
@ -95,7 +141,7 @@ export default function JeuxPage() {
|
||||||
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="ticketCode" className="block text-sm font-medium text-gray-700 mb-2">
|
<label htmlFor="ticketCode" className="block text-sm font-semibold text-gray-700 mb-2">
|
||||||
Code du ticket
|
Code du ticket
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -103,7 +149,7 @@ export default function JeuxPage() {
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="TTP2025ABC"
|
placeholder="TTP2025ABC"
|
||||||
{...register("ticketCode")}
|
{...register("ticketCode")}
|
||||||
className="w-full px-6 py-4 text-center text-2xl font-mono font-bold uppercase border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent tracking-widest"
|
className="w-full px-6 py-4 text-center text-2xl font-mono font-bold uppercase border-2 border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#1a4d2e] focus:border-transparent tracking-widest"
|
||||||
maxLength={10}
|
maxLength={10}
|
||||||
/>
|
/>
|
||||||
{errors.ticketCode && (
|
{errors.ticketCode && (
|
||||||
|
|
@ -115,7 +161,7 @@ export default function JeuxPage() {
|
||||||
❌ {errorMessage}
|
❌ {errorMessage}
|
||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
href={ROUTES.MY_LOTS}
|
href={ROUTES.HISTORY}
|
||||||
className="text-sm text-red-600 hover:text-red-800 underline font-medium"
|
className="text-sm text-red-600 hover:text-red-800 underline font-medium"
|
||||||
>
|
>
|
||||||
→ Voir vos tickets déjà utilisés
|
→ Voir vos tickets déjà utilisés
|
||||||
|
|
@ -128,15 +174,13 @@ export default function JeuxPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<Button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
isLoading={isPlaying}
|
|
||||||
disabled={isPlaying}
|
disabled={isPlaying}
|
||||||
size="lg"
|
className="bg-[#f59e0b] hover:bg-[#d97706] disabled:bg-gray-400 text-white font-bold px-12 py-4 text-lg rounded-lg transition-all shadow-lg hover:shadow-xl"
|
||||||
className="px-12 py-4 text-lg"
|
|
||||||
>
|
>
|
||||||
{isPlaying ? "Vérification en cours..." : "🎲 Tenter ma chance !"}
|
{isPlaying ? "Vérification en cours..." : "🎲 Tenter ma chance !"}
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
@ -146,29 +190,109 @@ export default function JeuxPage() {
|
||||||
Pas encore de compte ?
|
Pas encore de compte ?
|
||||||
</p>
|
</p>
|
||||||
<Link href={ROUTES.REGISTER}>
|
<Link href={ROUTES.REGISTER}>
|
||||||
<Button variant="outline" size="sm">
|
<button className="border-2 border-[#1a4d2e] text-[#1a4d2e] hover:bg-[#1a4d2e] hover:text-white font-bold px-6 py-2 rounded-lg transition-all">
|
||||||
Créer un compte gratuitement
|
Créer un compte gratuitement
|
||||||
</Button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isAuthenticated && (
|
{isAuthenticated && (
|
||||||
<div className="mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg">
|
<div className="mt-6 p-4 bg-blue-50 border-l-4 border-blue-500 rounded-lg">
|
||||||
<p className="text-sm text-blue-800 mb-2">
|
<p className="text-sm text-blue-800 font-semibold mb-2">
|
||||||
💡 <strong>Bon à savoir :</strong>
|
💡 Bon à savoir :
|
||||||
</p>
|
</p>
|
||||||
<ul className="text-sm text-blue-700 space-y-1 list-disc list-inside">
|
<ul className="text-sm text-blue-700 space-y-1 list-disc list-inside">
|
||||||
<li>Chaque code ne peut être utilisé qu'une seule fois</li>
|
<li>Chaque code ne peut être utilisé qu'une seule fois</li>
|
||||||
<li>Consultez vos tickets sur la page <Link href={ROUTES.MY_LOTS} className="underline font-medium">Mes lots</Link></li>
|
<li>Consultez vos tickets sur la page <Link href={ROUTES.HISTORY} className="underline font-medium hover:text-blue-900">Mes gains</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Roulette Modal */}
|
||||||
|
<Modal
|
||||||
|
isOpen={showRouletteModal}
|
||||||
|
onClose={() => {}}
|
||||||
|
title="🎰 Tirage en cours..."
|
||||||
|
size="md"
|
||||||
|
>
|
||||||
|
<div className="py-12">
|
||||||
|
<div className="flex flex-col items-center gap-8">
|
||||||
|
{/* Roulette Display */}
|
||||||
|
<div className="relative w-full max-w-sm">
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
{PRIZES.map((prize, index) => {
|
||||||
|
const getPrizeIcon = (type: string) => {
|
||||||
|
switch(type) {
|
||||||
|
case 'INFUSEUR':
|
||||||
|
return (
|
||||||
|
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
case 'THE_SIGNATURE':
|
||||||
|
return (
|
||||||
|
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
case 'COFFRET_DECOUVERTE':
|
||||||
|
return (
|
||||||
|
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
case 'COFFRET_PRESTIGE':
|
||||||
|
return (
|
||||||
|
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 2L4 5v6.09c0 5.05 3.41 9.76 8 10.91 4.59-1.15 8-5.86 8-10.91V5l-8-3zm6 9.09c0 4-2.55 7.7-6 8.83-3.45-1.13-6-4.82-6-8.83v-4.7l6-2.25 6 2.25v4.7zM8 10.5l1.5 1.5L15 6.5 13.5 5z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
case 'THE_GRATUIT':
|
||||||
|
return (
|
||||||
|
<svg className="w-8 h-8" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`flex items-center gap-4 p-4 rounded-xl transition-all duration-200 ${
|
||||||
|
currentPrizeIndex === index
|
||||||
|
? `${prize.color} scale-110 shadow-xl border-4 border-current`
|
||||||
|
: 'bg-gray-100 opacity-50 scale-95'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className={`w-12 h-12 rounded-full flex items-center justify-center ${prize.color}`}>
|
||||||
|
{getPrizeIcon(prize.type)}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className={`font-bold ${currentPrizeIndex === index ? 'text-lg' : 'text-sm'}`}>
|
||||||
|
{prize.name}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Loading Animation */}
|
||||||
|
<div className="flex items-center gap-2 text-gray-600">
|
||||||
|
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-[#1a4d2e]"></div>
|
||||||
|
<span className="font-medium">Tirage en cours...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
{/* Result Modal */}
|
{/* Result Modal */}
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={showResultModal}
|
isOpen={showResultModal}
|
||||||
|
|
@ -178,8 +302,36 @@ export default function JeuxPage() {
|
||||||
>
|
>
|
||||||
{gameResult && prizeConfig && (
|
{gameResult && prizeConfig && (
|
||||||
<div className="text-center py-6">
|
<div className="text-center py-6">
|
||||||
<div className="text-6xl mb-4">{prizeConfig.icon}</div>
|
<div className="flex justify-center mb-4">
|
||||||
<h3 className="text-2xl font-bold text-gray-900 mb-2">
|
<div className={`w-24 h-24 rounded-full flex items-center justify-center ${prizeConfig.color}`}>
|
||||||
|
{gameResult.prize?.type === 'INFUSEUR' && (
|
||||||
|
<svg className="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{gameResult.prize?.type === 'THE_SIGNATURE' && (
|
||||||
|
<svg className="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{gameResult.prize?.type === 'COFFRET_DECOUVERTE' && (
|
||||||
|
<svg className="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 6h-2.18c.11-.31.18-.65.18-1 0-1.66-1.34-3-3-3-1.05 0-1.96.54-2.5 1.35l-.5.67-.5-.68C10.96 2.54 10.05 2 9 2 7.34 2 6 3.34 6 5c0 .35.07.69.18 1H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-5-2c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM9 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm11 15H4v-2h16v2zm0-5H4V8h5.08L7 10.83 8.62 12 11 8.76l1-1.36 1 1.36L15.38 12 17 10.83 14.92 8H20v6z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{gameResult.prize?.type === 'COFFRET_PRESTIGE' && (
|
||||||
|
<svg className="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 2L4 5v6.09c0 5.05 3.41 9.76 8 10.91 4.59-1.15 8-5.86 8-10.91V5l-8-3zm6 9.09c0 4-2.55 7.7-6 8.83-3.45-1.13-6-4.82-6-8.83v-4.7l6-2.25 6 2.25v4.7zM8 10.5l1.5 1.5L15 6.5 13.5 5z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
{gameResult.prize?.type === 'THE_GRATUIT' && (
|
||||||
|
<svg className="w-12 h-12" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 3H4v10c0 2.21 1.79 4 4 4h6c2.21 0 4-1.79 4-4v-3h2c1.11 0 2-.9 2-2V5c0-1.11-.89-2-2-2zm0 5h-2V5h2v3zM4 19h16v2H4z"/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-3xl font-bold text-gray-900 mb-2">
|
||||||
Félicitations ! 🎉
|
Félicitations ! 🎉
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-lg text-gray-700 mb-4">
|
<p className="text-lg text-gray-700 mb-4">
|
||||||
|
|
@ -194,16 +346,23 @@ export default function JeuxPage() {
|
||||||
{gameResult.message || "Présentez-vous en magasin pour récupérer votre lot !"}
|
{gameResult.message || "Présentez-vous en magasin pour récupérer votre lot !"}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-3 justify-center">
|
<div className="flex gap-3 justify-center">
|
||||||
<Button onClick={closeModal} variant="outline">
|
<button
|
||||||
|
onClick={closeModal}
|
||||||
|
className="border-2 border-gray-300 hover:bg-gray-50 text-gray-700 font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
|
>
|
||||||
Fermer
|
Fermer
|
||||||
</Button>
|
</button>
|
||||||
<Button onClick={() => router.push(ROUTES.MY_LOTS)}>
|
<button
|
||||||
Voir mes lots
|
onClick={() => router.push(ROUTES.HISTORY)}
|
||||||
</Button>
|
className="bg-[#1a4d2e] hover:bg-[#2d5a3d] text-white font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
|
>
|
||||||
|
Voir mes gains
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,7 @@ import { useAuth } from "@/contexts/AuthContext";
|
||||||
import { loginSchema, LoginFormData } from "@/lib/validations";
|
import { loginSchema, LoginFormData } from "@/lib/validations";
|
||||||
import { Input } from "@/components/ui/Input";
|
import { Input } from "@/components/ui/Input";
|
||||||
import Button from "@/components/Button";
|
import Button from "@/components/Button";
|
||||||
import { Card } from "@/components/ui/Card";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
|
||||||
import { ROUTES } from "@/utils/constants";
|
import { ROUTES } from "@/utils/constants";
|
||||||
import { GoogleLoginButton } from "@/components/GoogleLoginButton";
|
import { GoogleLoginButton } from "@/components/GoogleLoginButton";
|
||||||
import { initFacebookSDK, loginWithFacebook } from "@/lib/facebook-sdk";
|
import { initFacebookSDK, loginWithFacebook } from "@/lib/facebook-sdk";
|
||||||
|
|
@ -21,12 +19,12 @@ export default function LoginPage() {
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [isFacebookLoading, setIsFacebookLoading] = useState(false);
|
const [isFacebookLoading, setIsFacebookLoading] = useState(false);
|
||||||
const [isFacebookSDKLoaded, setIsFacebookSDKLoaded] = useState(false);
|
const [isFacebookSDKLoaded, setIsFacebookSDKLoaded] = useState(false);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
const hasGoogleAuth = !!process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID;
|
const hasGoogleAuth = !!process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID;
|
||||||
const hasFacebookAuth = !!process.env.NEXT_PUBLIC_FACEBOOK_APP_ID;
|
const hasFacebookAuth = !!process.env.NEXT_PUBLIC_FACEBOOK_APP_ID;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Initialiser le SDK Facebook au chargement de la page
|
|
||||||
initFacebookSDK()
|
initFacebookSDK()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setIsFacebookSDKLoaded(true);
|
setIsFacebookSDKLoaded(true);
|
||||||
|
|
@ -77,112 +75,153 @@ export default function LoginPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-[calc(100vh-4rem)] flex items-center justify-center py-12 px-4">
|
<div className="min-h-screen bg-gray-50 flex items-center justify-center py-12 px-4">
|
||||||
<Card className="w-full max-w-md p-8">
|
<div className="w-full max-w-md">
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<div className="flex justify-center mb-6">
|
<h1 className="text-4xl font-bold text-gray-900 mb-2">Connexion</h1>
|
||||||
<Image
|
|
||||||
src="/logos/logo.svg"
|
|
||||||
alt="Thé Tip Top"
|
|
||||||
width={120}
|
|
||||||
height={120}
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Connexion</h1>
|
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
Connectez-vous pour participer au jeu-concours
|
Connectez-vous pour participer au jeu-concours
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
|
{/* Main Card */}
|
||||||
<Input
|
<div className="bg-white rounded-xl shadow-md overflow-hidden">
|
||||||
id="email"
|
|
||||||
type="email"
|
|
||||||
label="Email"
|
|
||||||
placeholder="votre.email@example.com"
|
|
||||||
error={errors.email?.message}
|
|
||||||
{...register("email")}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
{/* Tabs */}
|
||||||
id="password"
|
<div className="flex border-b border-gray-200">
|
||||||
type="password"
|
<button className="flex-1 py-4 px-6 text-center font-semibold text-gray-900 bg-white border-b-2 border-[#1a4d2e]">
|
||||||
label="Mot de passe"
|
Connexion
|
||||||
placeholder="••••••••"
|
</button>
|
||||||
error={errors.password?.message}
|
|
||||||
{...register("password")}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between text-sm">
|
|
||||||
<Link
|
<Link
|
||||||
href="/forgot-password"
|
href={ROUTES.REGISTER}
|
||||||
className="text-blue-600 hover:text-blue-700 hover:underline"
|
className="flex-1 py-4 px-6 text-center font-semibold text-gray-500 bg-gray-50 hover:bg-gray-100 transition-colors"
|
||||||
>
|
>
|
||||||
Mot de passe oublié ?
|
Inscription
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
{/* Form Container */}
|
||||||
type="submit"
|
<div className="p-8">
|
||||||
isLoading={isSubmitting}
|
|
||||||
disabled={isSubmitting}
|
|
||||||
fullWidth
|
|
||||||
size="lg"
|
|
||||||
>
|
|
||||||
Se connecter
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
{/* Social Login Buttons */}
|
||||||
{(hasGoogleAuth || hasFacebookAuth) && (
|
{(hasGoogleAuth || hasFacebookAuth) && (
|
||||||
<div className="mt-6">
|
<div className="space-y-3 mb-6">
|
||||||
<div className="relative">
|
|
||||||
<div className="absolute inset-0 flex items-center">
|
|
||||||
<div className="w-full border-t border-gray-300" />
|
|
||||||
</div>
|
|
||||||
<div className="relative flex justify-center text-sm">
|
|
||||||
<span className="px-2 bg-white text-gray-500">
|
|
||||||
Ou continuer avec
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`mt-6 grid ${hasGoogleAuth && hasFacebookAuth ? 'grid-cols-2' : 'grid-cols-1'} gap-3`}>
|
|
||||||
{hasGoogleAuth && (
|
{hasGoogleAuth && (
|
||||||
<GoogleLoginButton fullWidth />
|
<GoogleLoginButton fullWidth />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{hasFacebookAuth && (
|
{hasFacebookAuth && (
|
||||||
<Button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
|
||||||
onClick={handleFacebookLogin}
|
onClick={handleFacebookLogin}
|
||||||
disabled={isFacebookLoading || !isFacebookSDKLoaded}
|
disabled={isFacebookLoading || !isFacebookSDKLoaded}
|
||||||
isLoading={isFacebookLoading}
|
className="w-full flex items-center justify-center gap-3 px-4 py-3 border-2 border-primary-600 rounded-lg hover:bg-primary-50 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
fullWidth
|
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5 mr-2" fill="#1877F2" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="#1877F2" viewBox="0 0 24 24">
|
||||||
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" />
|
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" />
|
||||||
</svg>
|
</svg>
|
||||||
Facebook
|
<span className="font-medium text-primary-600">
|
||||||
</Button>
|
{isFacebookLoading ? "Connexion..." : "Continuer avec Facebook"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
{(hasGoogleAuth || hasFacebookAuth) && (
|
||||||
|
<div className="relative mb-6">
|
||||||
|
<div className="absolute inset-0 flex items-center">
|
||||||
|
<div className="w-full border-t border-gray-300" />
|
||||||
|
</div>
|
||||||
|
<div className="relative flex justify-center text-sm">
|
||||||
|
<span className="px-4 bg-white text-gray-500">
|
||||||
|
Ou avec votre email
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<p className="mt-8 text-center text-sm text-gray-600">
|
{/* Login Form */}
|
||||||
Vous n'avez pas de compte ?{" "}
|
<form onSubmit={handleSubmit(onSubmit)} className="space-y-5">
|
||||||
<Link
|
|
||||||
href={ROUTES.REGISTER}
|
{/* Email */}
|
||||||
className="font-medium text-blue-600 hover:text-blue-700 hover:underline"
|
<div>
|
||||||
|
<label htmlFor="email" className="block text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
Email <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
placeholder="votre@email.com"
|
||||||
|
{...register("email")}
|
||||||
|
className={`w-full px-4 py-3 border ${errors.email ? 'border-red-500' : 'border-gray-300'} rounded-lg focus:outline-none focus:ring-2 focus:ring-[#1a4d2e] focus:border-transparent`}
|
||||||
|
/>
|
||||||
|
{errors.email && (
|
||||||
|
<p className="mt-1 text-sm text-red-500">{errors.email.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Password */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="password" className="block text-sm font-semibold text-gray-700 mb-2">
|
||||||
|
Mot de passe <span className="text-red-500">*</span>
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
placeholder="••••••••"
|
||||||
|
{...register("password")}
|
||||||
|
className={`w-full px-4 py-3 border ${errors.password ? 'border-red-500' : 'border-gray-300'} rounded-lg focus:outline-none focus:ring-2 focus:ring-[#1a4d2e] focus:border-transparent pr-12`}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-700"
|
||||||
>
|
>
|
||||||
Créer un compte
|
{showPassword ? (
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21" />
|
||||||
|
</svg>
|
||||||
|
) : (
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{errors.password && (
|
||||||
|
<p className="mt-1 text-sm text-red-500">{errors.password.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
className="w-full bg-[#1a4d2e] hover:bg-[#2d5a3d] disabled:bg-gray-400 text-white font-bold px-8 py-4 rounded-lg transition-all"
|
||||||
|
>
|
||||||
|
{isSubmitting ? "Connexion..." : "Se connecter"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* Forgot Password */}
|
||||||
|
<div className="text-center">
|
||||||
|
<Link
|
||||||
|
href="/forgot-password"
|
||||||
|
className="text-sm text-[#1a4d2e] hover:text-[#f59e0b] hover:underline transition-colors"
|
||||||
|
>
|
||||||
|
Mot de passe oublié ?
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</div>
|
||||||
</Card>
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,17 +95,18 @@ export default function ProfilePage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8 max-w-4xl mx-auto">
|
<div className="min-h-screen bg-gray-50 py-8">
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-8">Mon profil</h1>
|
<div className="container mx-auto px-4 max-w-4xl">
|
||||||
|
<h1 className="text-4xl font-bold text-gray-900 mb-8">Mon profil</h1>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-3 gap-6">
|
<div className="grid md:grid-cols-3 gap-6">
|
||||||
{/* Profile Info Card */}
|
{/* Profile Info Card */}
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<Card>
|
<div className="bg-white rounded-xl shadow-md overflow-hidden">
|
||||||
<CardHeader>
|
<div className="px-6 py-4 border-b border-gray-200">
|
||||||
<CardTitle>Informations personnelles</CardTitle>
|
<h2 className="text-xl font-bold text-gray-900">Informations personnelles</h2>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent>
|
<div className="p-6">
|
||||||
{!isEditing ? (
|
{!isEditing ? (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -145,9 +146,12 @@ export default function ProfilePage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<Button onClick={() => setIsEditing(true)}>
|
<button
|
||||||
|
onClick={() => setIsEditing(true)}
|
||||||
|
className="bg-[#1a4d2e] hover:bg-[#2d5a3d] text-white font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
|
>
|
||||||
Modifier mes informations
|
Modifier mes informations
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -184,35 +188,35 @@ export default function ProfilePage() {
|
||||||
{...register("phone")}
|
{...register("phone")}
|
||||||
/>
|
/>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-3">
|
||||||
<Button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
isLoading={isSubmitting}
|
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
|
className="bg-[#1a4d2e] hover:bg-[#2d5a3d] disabled:bg-gray-400 text-white font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
Enregistrer
|
{isSubmitting ? "Enregistrement..." : "Enregistrer"}
|
||||||
</Button>
|
</button>
|
||||||
<Button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
|
className="border-2 border-gray-300 hover:bg-gray-50 text-gray-700 font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
Annuler
|
Annuler
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Account Status Card */}
|
{/* Account Status Card */}
|
||||||
<div>
|
<div>
|
||||||
<Card>
|
<div className="bg-white rounded-xl shadow-md overflow-hidden">
|
||||||
<CardHeader>
|
<div className="px-6 py-4 border-b border-gray-200">
|
||||||
<CardTitle>Statut du compte</CardTitle>
|
<h2 className="text-xl font-bold text-gray-900">Statut du compte</h2>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent className="space-y-4">
|
<div className="p-6 space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm font-medium text-gray-500">
|
<label className="text-sm font-medium text-gray-500">
|
||||||
Email vérifié
|
Email vérifié
|
||||||
|
|
@ -241,53 +245,50 @@ export default function ProfilePage() {
|
||||||
{user.updatedAt ? formatDate(user.updatedAt) : 'N/A'}
|
{user.updatedAt ? formatDate(user.updatedAt) : 'N/A'}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
|
||||||
{/* Quick Actions Card */}
|
{/* Quick Actions Card */}
|
||||||
<Card className="mt-6">
|
<div className="bg-white rounded-xl shadow-md overflow-hidden mt-6">
|
||||||
<CardHeader>
|
<div className="px-6 py-4 border-b border-gray-200">
|
||||||
<CardTitle>Actions rapides</CardTitle>
|
<h2 className="text-xl font-bold text-gray-900">Actions rapides</h2>
|
||||||
</CardHeader>
|
</div>
|
||||||
<CardContent className="space-y-2">
|
<div className="p-6 space-y-2">
|
||||||
{user.role === "CLIENT" && (
|
{user.role === "CLIENT" && (
|
||||||
<>
|
<>
|
||||||
<Button
|
<button
|
||||||
variant="outline"
|
|
||||||
fullWidth
|
|
||||||
onClick={() => router.push(ROUTES.GAME)}
|
onClick={() => router.push(ROUTES.GAME)}
|
||||||
|
className="w-full bg-[#f59e0b] hover:bg-[#d97706] text-white font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
Jouer
|
Jouer
|
||||||
</Button>
|
</button>
|
||||||
<Button
|
<button
|
||||||
variant="outline"
|
|
||||||
fullWidth
|
|
||||||
onClick={() => router.push(ROUTES.HISTORY)}
|
onClick={() => router.push(ROUTES.HISTORY)}
|
||||||
|
className="w-full border-2 border-[#1a4d2e] text-[#1a4d2e] hover:bg-[#1a4d2e] hover:text-white font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
Historique
|
Historique
|
||||||
</Button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{user.role === "EMPLOYEE" && (
|
{user.role === "EMPLOYEE" && (
|
||||||
<Button
|
<button
|
||||||
variant="outline"
|
|
||||||
fullWidth
|
|
||||||
onClick={() => router.push(ROUTES.EMPLOYEE_DASHBOARD)}
|
onClick={() => router.push(ROUTES.EMPLOYEE_DASHBOARD)}
|
||||||
|
className="w-full border-2 border-[#1a4d2e] text-[#1a4d2e] hover:bg-[#1a4d2e] hover:text-white font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
Tableau de bord
|
Tableau de bord
|
||||||
</Button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{user.role === "ADMIN" && (
|
{user.role === "ADMIN" && (
|
||||||
<Button
|
<button
|
||||||
variant="outline"
|
|
||||||
fullWidth
|
|
||||||
onClick={() => router.push(ROUTES.ADMIN_DASHBOARD)}
|
onClick={() => router.push(ROUTES.ADMIN_DASHBOARD)}
|
||||||
|
className="w-full border-2 border-[#1a4d2e] text-[#1a4d2e] hover:bg-[#1a4d2e] hover:text-white font-bold px-6 py-3 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
Administration
|
Administration
|
||||||
</Button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</div>
|
||||||
</Card>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,9 @@ export default function Header() {
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="flex items-center justify-between h-18">
|
<div className="flex items-center justify-between h-18">
|
||||||
{/* Logo */}
|
{/* Logo */}
|
||||||
<Link href={ROUTES.HOME} className="group">
|
<Link href={ROUTES.HOME} className="group flex items-center gap-3">
|
||||||
<Logo size="md" showText={true} className="group-hover:scale-105 transition-transform" />
|
<Logo size="md" showText={false} className="group-hover:scale-105 transition-transform" />
|
||||||
|
<span className="text-2xl font-bold text-[#f59e0b]">Thé Tip Top</span>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Desktop Navigation */}
|
{/* Desktop Navigation */}
|
||||||
|
|
@ -62,13 +63,13 @@ export default function Header() {
|
||||||
Accueil
|
Accueil
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={ROUTES.GAME}
|
href={ROUTES.LOTS}
|
||||||
className="text-white hover:text-[#f59e0b] font-medium transition-colors"
|
className="text-white hover:text-[#f59e0b] font-medium transition-colors"
|
||||||
>
|
>
|
||||||
Loto à gagner
|
Loto à gagner
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={ROUTES.LOTS}
|
href="/rules"
|
||||||
className="text-white hover:text-[#f59e0b] font-medium transition-colors"
|
className="text-white hover:text-[#f59e0b] font-medium transition-colors"
|
||||||
>
|
>
|
||||||
Règlement
|
Règlement
|
||||||
|
|
@ -80,7 +81,7 @@ export default function Header() {
|
||||||
FAQ
|
FAQ
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href="/about"
|
href="/gagnants"
|
||||||
className="text-white hover:text-[#f59e0b] font-medium transition-colors"
|
className="text-white hover:text-[#f59e0b] font-medium transition-colors"
|
||||||
>
|
>
|
||||||
Gagnants
|
Gagnants
|
||||||
|
|
@ -146,21 +147,30 @@ export default function Header() {
|
||||||
<div className="hidden md:flex items-center gap-3">
|
<div className="hidden md:flex items-center gap-3">
|
||||||
{isAuthenticated && (
|
{isAuthenticated && (
|
||||||
<>
|
<>
|
||||||
|
<Link href={getDashboardRoute()}>
|
||||||
|
<button className="flex flex-col items-center bg-white text-[#1a4d2e] hover:bg-[#f59e0b] hover:text-white font-semibold px-4 py-2 rounded-lg transition-all">
|
||||||
|
<span className="text-sm">{user?.firstName} {user?.lastName}</span>
|
||||||
|
<span className="text-xs font-normal opacity-80">{user?.email}</span>
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
<Link href={ROUTES.PROFILE}>
|
<Link href={ROUTES.PROFILE}>
|
||||||
<Button variant="outline" size="sm">
|
<button className="flex items-center gap-2 bg-white text-[#1a4d2e] hover:bg-[#f59e0b] hover:text-white font-semibold px-4 py-2 rounded-lg transition-all">
|
||||||
👤 {user?.firstName}
|
Profil
|
||||||
</Button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
{user?.role === 'CLIENT' && (
|
{user?.role === 'CLIENT' && (
|
||||||
<Link href={ROUTES.HISTORY}>
|
<Link href={ROUTES.HISTORY}>
|
||||||
<Button variant="outline" size="sm">
|
<button className="flex items-center gap-2 bg-white text-[#1a4d2e] hover:bg-[#f59e0b] hover:text-white font-semibold px-4 py-2 rounded-lg transition-all">
|
||||||
🏆 Mes gains
|
Mes gains
|
||||||
</Button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<Button variant="outline" size="sm" onClick={logout}>
|
<button
|
||||||
|
onClick={logout}
|
||||||
|
className="flex items-center gap-2 bg-white text-[#1a4d2e] hover:bg-red-600 hover:text-white font-semibold px-4 py-2 rounded-lg transition-all"
|
||||||
|
>
|
||||||
Déconnexion
|
Déconnexion
|
||||||
</Button>
|
</button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -297,36 +307,43 @@ export default function Header() {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{isAuthenticated && (
|
{isAuthenticated && (
|
||||||
<div className="border-t border-gray-200 pt-3 mt-3 space-y-2">
|
<div className="border-t border-white/20 pt-3 mt-3 space-y-2">
|
||||||
|
<Link
|
||||||
|
href={getDashboardRoute()}
|
||||||
|
onClick={() => setIsMobileMenuOpen(false)}
|
||||||
|
>
|
||||||
|
<button className="w-full flex flex-col items-center bg-white text-[#1a4d2e] hover:bg-[#f59e0b] hover:text-white font-semibold px-4 py-3 rounded-lg transition-all">
|
||||||
|
<span className="text-sm">{user?.firstName} {user?.lastName}</span>
|
||||||
|
<span className="text-xs font-normal opacity-80">{user?.email}</span>
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
href={ROUTES.PROFILE}
|
href={ROUTES.PROFILE}
|
||||||
onClick={() => setIsMobileMenuOpen(false)}
|
onClick={() => setIsMobileMenuOpen(false)}
|
||||||
>
|
>
|
||||||
<Button variant="outline" size="sm" fullWidth>
|
<button className="w-full flex items-center justify-center gap-2 bg-white text-[#1a4d2e] hover:bg-[#f59e0b] hover:text-white font-semibold px-4 py-3 rounded-lg transition-all">
|
||||||
👤 {user?.firstName}
|
Profil
|
||||||
</Button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
{user?.role === 'CLIENT' && (
|
{user?.role === 'CLIENT' && (
|
||||||
<Link
|
<Link
|
||||||
href={ROUTES.HISTORY}
|
href={ROUTES.HISTORY}
|
||||||
onClick={() => setIsMobileMenuOpen(false)}
|
onClick={() => setIsMobileMenuOpen(false)}
|
||||||
>
|
>
|
||||||
<Button variant="outline" size="sm" fullWidth>
|
<button className="w-full flex items-center justify-center gap-2 bg-white text-[#1a4d2e] hover:bg-[#f59e0b] hover:text-white font-semibold px-4 py-3 rounded-lg transition-all">
|
||||||
🏆 Mes gains
|
Mes gains
|
||||||
</Button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<Button
|
<button
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
fullWidth
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
logout();
|
logout();
|
||||||
setIsMobileMenuOpen(false);
|
setIsMobileMenuOpen(false);
|
||||||
}}
|
}}
|
||||||
|
className="w-full flex items-center justify-center gap-2 bg-white text-[#1a4d2e] hover:bg-red-600 hover:text-white font-semibold px-4 py-3 rounded-lg transition-all"
|
||||||
>
|
>
|
||||||
Déconnexion
|
Déconnexion
|
||||||
</Button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
|
|
@ -53,30 +53,35 @@ export const PRIZE_CONFIG = {
|
||||||
description: 'Un infuseur à thé de qualité',
|
description: 'Un infuseur à thé de qualité',
|
||||||
color: 'bg-blue-100 text-blue-800',
|
color: 'bg-blue-100 text-blue-800',
|
||||||
icon: '🫖',
|
icon: '🫖',
|
||||||
|
value: 15,
|
||||||
},
|
},
|
||||||
THE_SIGNATURE: {
|
THE_SIGNATURE: {
|
||||||
name: 'Thé signature 100g',
|
name: 'Thé signature 100g',
|
||||||
description: 'Notre thé signature premium 100g',
|
description: 'Notre thé signature premium 100g',
|
||||||
color: 'bg-green-100 text-green-800',
|
color: 'bg-green-100 text-green-800',
|
||||||
icon: '🍵',
|
icon: '🍵',
|
||||||
|
value: 25,
|
||||||
},
|
},
|
||||||
COFFRET_DECOUVERTE: {
|
COFFRET_DECOUVERTE: {
|
||||||
name: 'Coffret découverte 39€',
|
name: 'Coffret découverte 39€',
|
||||||
description: 'Un coffret découverte de nos meilleurs thés',
|
description: 'Un coffret découverte de nos meilleurs thés',
|
||||||
color: 'bg-purple-100 text-purple-800',
|
color: 'bg-purple-100 text-purple-800',
|
||||||
icon: '🎁',
|
icon: '🎁',
|
||||||
|
value: 39,
|
||||||
},
|
},
|
||||||
COFFRET_PRESTIGE: {
|
COFFRET_PRESTIGE: {
|
||||||
name: 'Coffret prestige 69€',
|
name: 'Coffret prestige 69€',
|
||||||
description: 'Un coffret prestige d\'exception',
|
description: 'Un coffret prestige d\'exception',
|
||||||
color: 'bg-amber-100 text-amber-800',
|
color: 'bg-amber-100 text-amber-800',
|
||||||
icon: '🏆',
|
icon: '🏆',
|
||||||
|
value: 69,
|
||||||
},
|
},
|
||||||
THE_GRATUIT: {
|
THE_GRATUIT: {
|
||||||
name: 'Thé gratuit en magasin',
|
name: 'Thé gratuit en magasin',
|
||||||
description: 'Un thé gratuit de votre choix en magasin',
|
description: 'Un thé gratuit de votre choix en magasin',
|
||||||
color: 'bg-pink-100 text-pink-800',
|
color: 'bg-pink-100 text-pink-800',
|
||||||
icon: '☕',
|
icon: '☕',
|
||||||
|
value: 0,
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user