feat: improve employee dashboard UI design
- Reorder sidebar: Dashboard first, then Validation des Tickets - Add gradient backgrounds and modern card designs - Replace emojis with SVG icons in statistics cards - Add avatars with initials for client display - Improve action cards with colored gradients (green, purple, slate) - Style search sections with gradient backgrounds - Add hover effects and transitions - Remove value display from prize details (already in name) - Improve filter buttons with gradient when active - Add zebra striping and better table styling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d60c03cb0e
commit
c578b81645
|
|
@ -97,109 +97,140 @@ export default function EmployeDashboardPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="py-8">
|
||||
<div className="min-h-full bg-gradient-to-br from-gray-50 via-white to-gray-50 p-8">
|
||||
{/* Welcome Section */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-4xl font-bold text-gray-900 mb-2">
|
||||
Tableau de bord employé
|
||||
<h1 className="text-4xl font-bold text-[#2d5a3d] mb-2">
|
||||
Bonjour {user?.firstName} ! 👋
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
Bienvenue {user?.firstName}, voici un aperçu de votre activité
|
||||
<p className="text-gray-600 text-lg">
|
||||
Bienvenue dans votre espace employé. Voici un aperçu de votre activité.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Statistics Cards */}
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
||||
<Card className="p-6">
|
||||
<div className="bg-white rounded-xl shadow-md p-6 border border-gray-100 hover:shadow-lg transition-shadow">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">
|
||||
<p className="text-sm font-medium text-gray-500 mb-2">
|
||||
Tickets en attente
|
||||
</p>
|
||||
<p className="text-3xl font-bold text-yellow-600 mt-2">
|
||||
<p className="text-4xl font-bold text-yellow-500">
|
||||
{stats.pendingTickets}
|
||||
</p>
|
||||
</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-500" 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>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card className="p-6">
|
||||
<div className="bg-white rounded-xl shadow-md p-6 border border-gray-100 hover:shadow-lg transition-shadow">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">
|
||||
<p className="text-sm font-medium text-gray-500 mb-2">
|
||||
Réclamés aujourd'hui
|
||||
</p>
|
||||
<p className="text-3xl font-bold text-green-600 mt-2">
|
||||
<p className="text-4xl font-bold text-green-600">
|
||||
{stats.claimedToday}
|
||||
</p>
|
||||
</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>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card className="p-6">
|
||||
<div className="bg-white rounded-xl shadow-md p-6 border border-gray-100 hover:shadow-lg transition-shadow">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">
|
||||
<p className="text-sm font-medium text-gray-500 mb-2">
|
||||
Total réclamés
|
||||
</p>
|
||||
<p className="text-3xl font-bold text-blue-600 mt-2">
|
||||
<p className="text-4xl font-bold text-blue-600">
|
||||
{stats.totalClaimed}
|
||||
</p>
|
||||
</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>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions Title */}
|
||||
<div className="mb-6">
|
||||
<h2 className="text-2xl font-bold text-gray-800">Actions rapides</h2>
|
||||
<p className="text-gray-500">Accédez rapidement aux fonctionnalités principales</p>
|
||||
</div>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<div className="grid md:grid-cols-3 gap-6">
|
||||
<Link href="/employe/verification">
|
||||
<Card className="p-6 hover:shadow-lg transition-shadow cursor-pointer">
|
||||
<div className="flex items-center">
|
||||
<div className="text-5xl mr-4">🔍</div>
|
||||
<div className="bg-gradient-to-br from-green-600 to-green-800 text-white rounded-xl shadow-md p-6 hover:shadow-xl transition-all hover:scale-[1.02] cursor-pointer">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 bg-white/20 rounded-xl flex items-center justify-center">
|
||||
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">
|
||||
<h3 className="text-xl font-bold mb-1">
|
||||
Validation des gains
|
||||
</h3>
|
||||
<p className="text-gray-600">
|
||||
Rechercher et valider les tickets des clients
|
||||
<p className="text-white/80 text-sm">
|
||||
Rechercher et valider les tickets
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link href="/employe/gains-client">
|
||||
<Card className="p-6 hover:shadow-lg transition-shadow cursor-pointer">
|
||||
<div className="flex items-center">
|
||||
<div className="text-5xl mr-4">🎁</div>
|
||||
<div className="bg-gradient-to-br from-purple-600 to-purple-800 text-white rounded-xl shadow-md p-6 hover:shadow-xl transition-all hover:scale-[1.02] cursor-pointer">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 bg-white/20 rounded-xl flex items-center justify-center">
|
||||
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v13m0-13V6a2 2 0 112 2h-2zm0 0V5.5A2.5 2.5 0 109.5 8H12zm-7 4h14M5 12a2 2 0 110-4h14a2 2 0 110 4M5 12v7a2 2 0 002 2h10a2 2 0 002-2v-7" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">
|
||||
<h3 className="text-xl font-bold mb-1">
|
||||
Gains du Client
|
||||
</h3>
|
||||
<p className="text-gray-600">
|
||||
Rechercher tous les gains d'un client
|
||||
<p className="text-white/80 text-sm">
|
||||
Rechercher les gains d'un client
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
<Link href="/employe/historique">
|
||||
<Card className="p-6 hover:shadow-lg transition-shadow cursor-pointer">
|
||||
<div className="flex items-center">
|
||||
<div className="text-5xl mr-4">📜</div>
|
||||
<div className="bg-gradient-to-br from-slate-600 to-slate-800 text-white rounded-xl shadow-md p-6 hover:shadow-xl transition-all hover:scale-[1.02] cursor-pointer">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 bg-white/20 rounded-xl flex items-center justify-center">
|
||||
<svg className="w-7 h-7 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-bold text-gray-900 mb-2">
|
||||
<h3 className="text-xl font-bold mb-1">
|
||||
Historique
|
||||
</h3>
|
||||
<p className="text-gray-600">
|
||||
<p className="text-white/80 text-sm">
|
||||
Consulter l'historique des validations
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -94,49 +94,55 @@ export default function GainsClientPage() {
|
|||
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-7xl mx-auto">
|
||||
<div className="min-h-full bg-gradient-to-br from-gray-50 via-white to-gray-50 p-8">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<h1 className="text-3xl font-bold mb-2 flex items-center gap-3">
|
||||
<Gift className="w-10 h-10 text-purple-600" />
|
||||
<div className="mb-8">
|
||||
<h1 className="text-4xl font-bold text-[#2d5a3d] mb-2">
|
||||
Gains du Client
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
<p className="text-gray-600 text-lg">
|
||||
Recherchez un client pour visualiser tous ses gains et les remettre
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Search Section */}
|
||||
<Card className="p-6 mb-6">
|
||||
<h2 className="text-xl font-bold mb-4">Rechercher un client</h2>
|
||||
<div className="bg-gradient-to-r from-purple-600 to-purple-700 rounded-2xl shadow-lg p-8 mb-8">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-10 h-10 bg-white/20 rounded-lg flex items-center justify-center">
|
||||
<Search className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<h2 className="text-xl font-bold text-white">
|
||||
Rechercher un client
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{/* Search Type Selection */}
|
||||
<div className="flex gap-4">
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<div className="flex gap-6">
|
||||
<label className={`flex items-center gap-3 cursor-pointer px-4 py-2 rounded-lg transition-all ${searchType === 'email' ? 'bg-white/20' : 'hover:bg-white/10'}`}>
|
||||
<input
|
||||
type="radio"
|
||||
name="searchType"
|
||||
value="email"
|
||||
checked={searchType === 'email'}
|
||||
onChange={(e) => setSearchType(e.target.value as 'email')}
|
||||
className="w-4 h-4"
|
||||
className="w-4 h-4 accent-white"
|
||||
/>
|
||||
<Mail className="w-5 h-5 text-gray-600" />
|
||||
<span className="text-sm font-medium">Email</span>
|
||||
<Mail className="w-5 h-5 text-white" />
|
||||
<span className="text-sm font-medium text-white">Email</span>
|
||||
</label>
|
||||
|
||||
<label className="flex items-center gap-2 cursor-pointer">
|
||||
<label className={`flex items-center gap-3 cursor-pointer px-4 py-2 rounded-lg transition-all ${searchType === 'phone' ? 'bg-white/20' : 'hover:bg-white/10'}`}>
|
||||
<input
|
||||
type="radio"
|
||||
name="searchType"
|
||||
value="phone"
|
||||
checked={searchType === 'phone'}
|
||||
onChange={(e) => setSearchType(e.target.value as 'phone')}
|
||||
className="w-4 h-4"
|
||||
className="w-4 h-4 accent-white"
|
||||
/>
|
||||
<Phone className="w-5 h-5 text-gray-600" />
|
||||
<span className="text-sm font-medium">Téléphone</span>
|
||||
<Phone className="w-5 h-5 text-white" />
|
||||
<span className="text-sm font-medium text-white">Téléphone</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
|
@ -148,30 +154,29 @@ export default function GainsClientPage() {
|
|||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
|
||||
className="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
className="flex-1 px-5 py-4 border-2 border-white/20 bg-white/10 text-white placeholder-white/60 rounded-xl focus:ring-2 focus:ring-white focus:border-transparent text-lg backdrop-blur-sm"
|
||||
/>
|
||||
<Button
|
||||
<button
|
||||
onClick={handleSearch}
|
||||
isLoading={loading}
|
||||
disabled={loading}
|
||||
className="bg-purple-600 hover:bg-purple-700"
|
||||
className="flex items-center gap-2 px-8 py-4 bg-white text-purple-700 rounded-xl hover:bg-purple-50 transition-all font-bold shadow-lg hover:shadow-xl hover:scale-[1.02] disabled:opacity-50"
|
||||
>
|
||||
<Search className="w-5 h-5 mr-2" />
|
||||
Rechercher
|
||||
</Button>
|
||||
<Search className="w-5 h-5" />
|
||||
{loading ? 'Recherche...' : 'Rechercher'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Client Info & Prizes */}
|
||||
{clientData && (
|
||||
<>
|
||||
{/* Client Info Card */}
|
||||
<Card className="p-6 mb-6 bg-gradient-to-r from-purple-50 to-blue-50 border-purple-200">
|
||||
<div className="bg-white rounded-2xl shadow-md border border-gray-100 p-6 mb-6">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="p-3 bg-white rounded-lg shadow-sm">
|
||||
<User className="w-8 h-8 text-purple-600" />
|
||||
<div className="w-16 h-16 bg-gradient-to-br from-purple-500 to-purple-600 rounded-xl flex items-center justify-center text-white font-bold text-xl shadow-lg">
|
||||
{clientData.client.firstName?.charAt(0)}{clientData.client.lastName?.charAt(0)}
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900">
|
||||
|
|
@ -179,12 +184,12 @@ export default function GainsClientPage() {
|
|||
</h2>
|
||||
<div className="mt-2 space-y-1">
|
||||
<p className="text-sm text-gray-600 flex items-center gap-2">
|
||||
<Mail className="w-4 h-4" />
|
||||
<Mail className="w-4 h-4 text-purple-500" />
|
||||
{clientData.client.email}
|
||||
</p>
|
||||
{clientData.client.phone && (
|
||||
<p className="text-sm text-gray-600 flex items-center gap-2">
|
||||
<Phone className="w-4 h-4" />
|
||||
<Phone className="w-4 h-4 text-purple-500" />
|
||||
{clientData.client.phone}
|
||||
</p>
|
||||
)}
|
||||
|
|
@ -192,108 +197,116 @@ export default function GainsClientPage() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-right">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="bg-white rounded-lg p-3 shadow-sm">
|
||||
<p className="text-xs text-gray-600">Total</p>
|
||||
<p className="text-2xl font-bold text-gray-900">{clientData.totalPrizes}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg p-3 shadow-sm">
|
||||
<p className="text-xs text-yellow-600">À remettre</p>
|
||||
<p className="text-2xl font-bold text-yellow-600">{clientData.pendingPrizes}</p>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg p-3 shadow-sm">
|
||||
<p className="text-xs text-green-600">Remis</p>
|
||||
<p className="text-2xl font-bold text-green-600">{clientData.claimedPrizes}</p>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="bg-gray-50 rounded-xl p-4 text-center min-w-[100px]">
|
||||
<p className="text-xs font-medium text-gray-500 mb-1">Total</p>
|
||||
<p className="text-3xl font-bold text-gray-900">{clientData.totalPrizes}</p>
|
||||
</div>
|
||||
<div className="bg-yellow-50 rounded-xl p-4 text-center min-w-[100px]">
|
||||
<p className="text-xs font-medium text-yellow-600 mb-1">À remettre</p>
|
||||
<p className="text-3xl font-bold text-yellow-600">{clientData.pendingPrizes}</p>
|
||||
</div>
|
||||
<div className="bg-green-50 rounded-xl p-4 text-center min-w-[100px]">
|
||||
<p className="text-xs font-medium text-green-600 mb-1">Remis</p>
|
||||
<p className="text-3xl font-bold text-green-600">{clientData.claimedPrizes}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Prizes List */}
|
||||
<Card className="p-6">
|
||||
<h2 className="text-xl font-bold mb-4 flex items-center gap-2">
|
||||
<Package className="w-6 h-6 text-purple-600" />
|
||||
Lots gagnés ({clientData.prizes.length})
|
||||
</h2>
|
||||
<div className="bg-white rounded-2xl shadow-md border border-gray-100 overflow-hidden">
|
||||
<div className="px-6 py-5 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
|
||||
<Gift className="w-5 h-5 text-purple-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-800">
|
||||
Lots gagnés
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">{clientData.prizes.length} lot(s) au total</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{clientData.prizes.length === 0 ? (
|
||||
<EmptyState
|
||||
icon="🎁"
|
||||
message="Ce client n'a pas encore gagné de lots"
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{clientData.prizes.map((prize) => (
|
||||
<div
|
||||
key={prize.ticketId}
|
||||
className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<h3 className="text-lg font-bold text-gray-900">
|
||||
{prize.prize.name}
|
||||
</h3>
|
||||
<StatusBadge type="ticket" value={prize.status} showIcon />
|
||||
</div>
|
||||
<div className="p-6">
|
||||
{clientData.prizes.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="w-16 h-16 bg-purple-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Gift className="w-8 h-8 text-purple-600" />
|
||||
</div>
|
||||
<p className="text-gray-600 font-medium">Ce client n'a pas encore gagné de lots</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{clientData.prizes.map((prize) => (
|
||||
<div
|
||||
key={prize.ticketId}
|
||||
className={`border-2 rounded-xl p-5 transition-all hover:shadow-md ${
|
||||
prize.status === 'PENDING'
|
||||
? 'border-yellow-200 bg-yellow-50/30'
|
||||
: 'border-gray-100 bg-white'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<h3 className="text-lg font-bold text-gray-900">
|
||||
{prize.prize.name}
|
||||
</h3>
|
||||
<StatusBadge type="ticket" value={prize.status} showIcon />
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-600 mb-3">
|
||||
{prize.prize.description}
|
||||
</p>
|
||||
<p className="text-sm text-gray-600 mb-4">
|
||||
{prize.prize.description}
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<span className="text-gray-600">Code ticket:</span>
|
||||
<span className="ml-2 font-mono font-semibold">{prize.ticketCode}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">Valeur:</span>
|
||||
<span className="ml-2 font-semibold text-purple-600">
|
||||
{prize.prize.value}€
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-600">Gagné le:</span>
|
||||
<span className="ml-2">
|
||||
{new Date(prize.playedAt).toLocaleDateString('fr-FR')}
|
||||
</span>
|
||||
</div>
|
||||
{prize.claimedAt && (
|
||||
<div>
|
||||
<span className="text-gray-600">Remis le:</span>
|
||||
<span className="ml-2">
|
||||
{new Date(prize.claimedAt).toLocaleDateString('fr-FR')}
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-500">Code:</span>
|
||||
<span className="font-mono font-semibold bg-gray-100 px-2 py-1 rounded">{prize.ticketCode}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-500">Gagné le:</span>
|
||||
<span className="font-medium">
|
||||
{new Date(prize.playedAt).toLocaleDateString('fr-FR')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{prize.validatedBy && (
|
||||
<div className="col-span-2">
|
||||
<span className="text-gray-600">Remis par:</span>
|
||||
<span className="ml-2 font-medium">{prize.validatedBy}</span>
|
||||
</div>
|
||||
)}
|
||||
{prize.claimedAt && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-500">Remis le:</span>
|
||||
<span className="font-medium text-green-600">
|
||||
{new Date(prize.claimedAt).toLocaleDateString('fr-FR')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{prize.validatedBy && (
|
||||
<div className="col-span-2 flex items-center gap-2">
|
||||
<span className="text-gray-500">Remis par:</span>
|
||||
<span className="font-medium">{prize.validatedBy}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{prize.status === 'PENDING' && (
|
||||
<Button
|
||||
onClick={() => handleValidatePrize(prize.ticketId)}
|
||||
isLoading={validatingTicketId === prize.ticketId}
|
||||
disabled={validatingTicketId === prize.ticketId}
|
||||
className="ml-4 bg-green-600 hover:bg-green-700"
|
||||
>
|
||||
<CheckCircle className="w-5 h-5 mr-2" />
|
||||
Marquer comme remis
|
||||
</Button>
|
||||
)}
|
||||
{prize.status === 'PENDING' && (
|
||||
<button
|
||||
onClick={() => handleValidatePrize(prize.ticketId)}
|
||||
disabled={validatingTicketId === prize.ticketId}
|
||||
className="ml-4 flex items-center gap-2 px-5 py-3 bg-gradient-to-r from-green-600 to-green-700 text-white font-semibold rounded-xl hover:from-green-700 hover:to-green-800 transition-all shadow-sm hover:shadow-md disabled:opacity-50"
|
||||
>
|
||||
<CheckCircle className="w-5 h-5" />
|
||||
{validatingTicketId === prize.ticketId ? 'Validation...' : 'Marquer comme remis'}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -73,148 +73,169 @@ export default function EmployeeHistoryPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-7xl mx-auto">
|
||||
<div className="min-h-full bg-gradient-to-br from-gray-50 via-white to-gray-50 p-8">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<h1 className="text-3xl font-bold mb-2 flex items-center gap-3">
|
||||
<Calendar className="w-10 h-10 text-purple-600" />
|
||||
<div className="mb-8">
|
||||
<h1 className="text-4xl font-bold text-[#2d5a3d] mb-2">
|
||||
Historique des Validations
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
<p className="text-gray-600 text-lg">
|
||||
Consultez l'historique de vos validations de tickets
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Statistics Cards */}
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-6">
|
||||
<Card className="p-6 bg-gradient-to-br from-blue-50 to-blue-100">
|
||||
<div className="grid md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white rounded-xl shadow-md p-6 border border-gray-100 hover:shadow-lg transition-shadow">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Total traités</p>
|
||||
<p className="text-3xl font-bold text-blue-600 mt-2">{stats.total}</p>
|
||||
<p className="text-sm font-medium text-gray-500 mb-2">Total traités</p>
|
||||
<p className="text-4xl font-bold text-blue-600">{stats.total}</p>
|
||||
</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 className="text-4xl">📊</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card className="p-6 bg-gradient-to-br from-green-50 to-green-100">
|
||||
<div className="bg-white rounded-xl shadow-md p-6 border border-gray-100 hover:shadow-lg transition-shadow">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Validés</p>
|
||||
<p className="text-3xl font-bold text-green-600 mt-2">{stats.claimed}</p>
|
||||
<p className="text-sm font-medium text-gray-500 mb-2">Validé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 className="text-4xl">✅</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Card className="p-6 bg-gradient-to-br from-red-50 to-red-100">
|
||||
<div className="bg-white rounded-xl shadow-md p-6 border border-gray-100 hover:shadow-lg transition-shadow">
|
||||
<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>
|
||||
<p className="text-sm font-medium text-gray-500 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 className="text-4xl">❌</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Filters */}
|
||||
<Card className="p-4 mb-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex gap-2">
|
||||
{/* Filters & History List */}
|
||||
<div className="bg-white rounded-2xl shadow-md border border-gray-100 overflow-hidden">
|
||||
<div className="px-6 py-5 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setFilter('ALL')}
|
||||
className={`px-5 py-2.5 rounded-xl font-semibold transition-all ${
|
||||
filter === 'ALL'
|
||||
? 'bg-gradient-to-r from-slate-600 to-slate-700 text-white shadow-md'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
Tous ({history.length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('CLAIMED')}
|
||||
className={`px-5 py-2.5 rounded-xl font-semibold transition-all ${
|
||||
filter === 'CLAIMED'
|
||||
? 'bg-gradient-to-r from-green-600 to-green-700 text-white shadow-md'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
Validés ({stats.claimed})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('REJECTED')}
|
||||
className={`px-5 py-2.5 rounded-xl font-semibold transition-all ${
|
||||
filter === 'REJECTED'
|
||||
? 'bg-gradient-to-r from-red-600 to-red-700 text-white shadow-md'
|
||||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
Rejetés ({stats.rejected})
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setFilter('ALL')}
|
||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
||||
filter === 'ALL'
|
||||
? 'bg-purple-600 text-white'
|
||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||
}`}
|
||||
onClick={loadHistory}
|
||||
className="flex items-center gap-2 px-4 py-2 text-green-700 bg-green-50 hover:bg-green-100 rounded-lg transition-colors font-medium"
|
||||
>
|
||||
Tous ({history.length})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('CLAIMED')}
|
||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
||||
filter === 'CLAIMED'
|
||||
? 'bg-green-600 text-white'
|
||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
Validés ({stats.claimed})
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setFilter('REJECTED')}
|
||||
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
|
||||
filter === 'REJECTED'
|
||||
? 'bg-red-600 text-white'
|
||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
Rejetés ({stats.rejected})
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
Actualiser
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={loadHistory}
|
||||
className="flex items-center gap-2 px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
Actualiser
|
||||
</button>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* History List */}
|
||||
<Card className="p-6">
|
||||
{filteredHistory.length === 0 ? (
|
||||
<EmptyState
|
||||
icon="📝"
|
||||
title="Aucun ticket dans l'historique"
|
||||
message={filter === 'ALL'
|
||||
? 'Vous n\'avez pas encore validé de tickets'
|
||||
: `Aucun ticket ${filter === 'CLAIMED' ? 'validé' : 'rejeté'}`}
|
||||
/>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{filteredHistory.map((ticket) => (
|
||||
<div
|
||||
key={ticket.id}
|
||||
className="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<span className="font-mono font-bold text-lg text-gray-900">
|
||||
{ticket.code}
|
||||
</span>
|
||||
<StatusBadge type="ticket" value={ticket.status} showIcon />
|
||||
</div>
|
||||
<div className="p-6">
|
||||
{filteredHistory.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<Calendar className="w-8 h-8 text-gray-400" />
|
||||
</div>
|
||||
<p className="text-gray-800 font-semibold mb-1">Aucun ticket dans l'historique</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
{filter === 'ALL'
|
||||
? 'Vous n\'avez pas encore validé de tickets'
|
||||
: `Aucun ticket ${filter === 'CLAIMED' ? 'validé' : 'rejeté'}`}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{filteredHistory.map((ticket) => (
|
||||
<div
|
||||
key={ticket.id}
|
||||
className={`border-2 rounded-xl p-5 transition-all hover:shadow-md ${
|
||||
ticket.status === 'REJECTED'
|
||||
? 'border-red-200 bg-red-50/30'
|
||||
: 'border-gray-100 bg-white'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<span className="font-mono font-bold text-lg bg-gray-100 px-3 py-1 rounded-lg text-gray-900">
|
||||
{ticket.code}
|
||||
</span>
|
||||
<StatusBadge type="ticket" value={ticket.status} showIcon />
|
||||
</div>
|
||||
|
||||
<div className="grid md:grid-cols-2 gap-4 mb-3">
|
||||
<div className="flex items-start gap-2">
|
||||
<User className="w-5 h-5 text-gray-400 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Client</p>
|
||||
<p className="text-sm text-gray-900">{ticket.user_name}</p>
|
||||
<p className="text-xs text-gray-500">{ticket.user_email}</p>
|
||||
<div className="grid md:grid-cols-2 gap-6 mb-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 bg-gradient-to-br from-green-500 to-green-600 rounded-full flex items-center justify-center text-white font-bold text-sm flex-shrink-0">
|
||||
{ticket.user_name.split(' ').map(n => n.charAt(0)).join('').slice(0, 2)}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-medium text-gray-500 mb-1">Client</p>
|
||||
<p className="text-sm font-semibold text-gray-900">{ticket.user_name}</p>
|
||||
<p className="text-xs text-gray-500">{ticket.user_email}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3">
|
||||
<div className="w-10 h-10 bg-purple-100 rounded-full flex items-center justify-center flex-shrink-0">
|
||||
<Gift className="w-5 h-5 text-purple-600" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs font-medium text-gray-500 mb-1">Lot</p>
|
||||
<p className="text-sm font-semibold text-gray-900">{ticket.prize_name}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-2">
|
||||
<Gift className="w-5 h-5 text-gray-400 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Lot</p>
|
||||
<p className="text-sm text-gray-900">{ticket.prize_name}</p>
|
||||
<p className="text-xs text-gray-500">{ticket.prize_value}€</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 text-xs text-gray-500">
|
||||
<div className="flex items-center gap-1">
|
||||
<Calendar className="w-3 h-3" />
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<span>
|
||||
Validé le{' '}
|
||||
{ticket.status === 'REJECTED' ? 'Rejeté' : 'Validé'} le{' '}
|
||||
{ticket.validated_at
|
||||
? new Date(ticket.validated_at).toLocaleDateString('fr-FR', {
|
||||
day: 'numeric',
|
||||
|
|
@ -226,23 +247,23 @@ export default function EmployeeHistoryPage() {
|
|||
: 'N/A'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{ticket.rejection_reason && (
|
||||
<div className="mt-3 p-3 bg-red-50 border border-red-200 rounded-lg">
|
||||
<p className="text-sm font-medium text-red-800 mb-1">
|
||||
Raison du rejet:
|
||||
</p>
|
||||
<p className="text-sm text-red-700">{ticket.rejection_reason}</p>
|
||||
</div>
|
||||
)}
|
||||
{ticket.rejection_reason && (
|
||||
<div className="mt-4 p-4 bg-red-50 border border-red-200 rounded-xl">
|
||||
<p className="text-sm font-semibold text-red-800 mb-1">
|
||||
Raison du rejet:
|
||||
</p>
|
||||
<p className="text-sm text-red-700">{ticket.rejection_reason}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,16 +51,16 @@ export default function EmployeLayout({
|
|||
}
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
label: "Validation des Tickets",
|
||||
href: "/employe/verification",
|
||||
icon: <Ticket className="w-5 h-5" />,
|
||||
},
|
||||
{
|
||||
label: "Dashboard",
|
||||
href: "/employe/dashboard",
|
||||
icon: <BarChart3 className="w-5 h-5" />,
|
||||
},
|
||||
{
|
||||
label: "Validation des Tickets",
|
||||
href: "/employe/verification",
|
||||
icon: <Ticket className="w-5 h-5" />,
|
||||
},
|
||||
];
|
||||
|
||||
const isActive = (href: string) => pathname === href;
|
||||
|
|
|
|||
|
|
@ -125,37 +125,42 @@ export default function EmployeeVerificationPage() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="p-8 bg-gray-50 min-h-screen">
|
||||
<div className="min-h-full bg-gradient-to-br from-gray-50 via-white to-gray-50 p-8">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">
|
||||
<h1 className="text-4xl font-bold text-[#2d5a3d] mb-2">
|
||||
Validation des Tickets
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
<p className="text-gray-600 text-lg">
|
||||
Scannez ou recherchez un code pour valider les lots gagnés
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Search Section */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
|
||||
<h2 className="text-lg font-semibold text-gray-900 mb-4">
|
||||
Rechercher un ticket
|
||||
</h2>
|
||||
<div className="bg-gradient-to-r from-green-600 to-green-700 rounded-2xl shadow-lg p-8 mb-8">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-10 h-10 bg-white/20 rounded-lg flex items-center justify-center">
|
||||
<Search className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<h2 className="text-xl font-bold text-white">
|
||||
Rechercher un ticket
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="flex-1">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Entrez le code du ticket (ex: ABC123)"
|
||||
placeholder="Entrez le code du ticket (ex: TTABC123)"
|
||||
value={searchCode}
|
||||
onChange={(e) => setSearchCode(e.target.value.toUpperCase())}
|
||||
onKeyPress={(e) => e.key === "Enter" && handleSearch()}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent font-mono text-lg"
|
||||
maxLength={10}
|
||||
className="w-full px-5 py-4 border-2 border-white/20 bg-white/10 text-white placeholder-white/60 rounded-xl focus:ring-2 focus:ring-white focus:border-transparent font-mono text-lg backdrop-blur-sm"
|
||||
maxLength={12}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSearch}
|
||||
className="flex items-center gap-2 px-6 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors font-medium"
|
||||
className="flex items-center gap-2 px-8 py-4 bg-white text-green-700 rounded-xl hover:bg-green-50 transition-all font-bold shadow-lg hover:shadow-xl hover:scale-[1.02]"
|
||||
>
|
||||
<Search className="w-5 h-5" />
|
||||
Rechercher
|
||||
|
|
@ -164,14 +169,22 @@ export default function EmployeeVerificationPage() {
|
|||
</div>
|
||||
|
||||
{/* Lots en attente de remise */}
|
||||
<div className="bg-white rounded-lg shadow-sm border border-gray-200">
|
||||
<div className="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold text-gray-900">
|
||||
Lots en attente de remise
|
||||
</h2>
|
||||
<div className="bg-white rounded-2xl shadow-md border border-gray-100 overflow-hidden">
|
||||
<div className="px-6 py-5 border-b border-gray-100 flex items-center justify-between bg-gradient-to-r from-gray-50 to-white">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-yellow-100 rounded-lg flex items-center justify-center">
|
||||
<Clock className="w-5 h-5 text-yellow-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-gray-800">
|
||||
Lots en attente de remise
|
||||
</h2>
|
||||
<p className="text-sm text-gray-500">{tickets.length} ticket(s) en attente</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={loadPendingTickets}
|
||||
className="flex items-center gap-2 px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-lg transition-colors"
|
||||
className="flex items-center gap-2 px-4 py-2 text-green-700 bg-green-50 hover:bg-green-100 rounded-lg transition-colors font-medium"
|
||||
>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
Actualiser
|
||||
|
|
@ -181,8 +194,10 @@ export default function EmployeeVerificationPage() {
|
|||
<div className="overflow-x-auto">
|
||||
{tickets.length === 0 ? (
|
||||
<div className="text-center py-16">
|
||||
<div className="text-6xl mb-4">✨</div>
|
||||
<p className="text-gray-600 mb-2 font-medium">
|
||||
<div className="w-20 h-20 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<CheckCircle className="w-10 h-10 text-green-600" />
|
||||
</div>
|
||||
<p className="text-gray-800 mb-2 font-semibold text-lg">
|
||||
Aucun lot en attente
|
||||
</p>
|
||||
<p className="text-sm text-gray-500">
|
||||
|
|
@ -190,60 +205,70 @@ export default function EmployeeVerificationPage() {
|
|||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-gray-50 border-b border-gray-100">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">
|
||||
Client
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">
|
||||
Lot Gagné
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-left text-xs font-bold text-gray-500 uppercase tracking-wider">
|
||||
Date
|
||||
</th>
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-4 text-right text-xs font-bold text-gray-500 uppercase tracking-wider">
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{tickets.map((ticket) => (
|
||||
<tr key={ticket.id} className="hover:bg-gray-50">
|
||||
<td className="px-6 py-4">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{ticket.user ? `${ticket.user.firstName} ${ticket.user.lastName}` : 'N/A'}
|
||||
<tbody className="divide-y divide-gray-50">
|
||||
{tickets.map((ticket, index) => (
|
||||
<tr key={ticket.id} className={`hover:bg-green-50/50 transition-colors ${index % 2 === 0 ? 'bg-white' : 'bg-gray-50/30'}`}>
|
||||
<td className="px-6 py-5">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 bg-gradient-to-br from-green-500 to-green-600 rounded-full flex items-center justify-center text-white font-bold text-sm">
|
||||
{ticket.user ? `${ticket.user.firstName?.charAt(0) || ''}${ticket.user.lastName?.charAt(0) || ''}` : 'N/A'}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{ticket.user?.email || 'N/A'}
|
||||
<div>
|
||||
<div className="text-sm font-semibold text-gray-900">
|
||||
{ticket.user ? `${ticket.user.firstName} ${ticket.user.lastName}` : 'N/A'}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{ticket.user?.email || 'N/A'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4">
|
||||
<td className="px-6 py-5">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
<div className="text-sm font-semibold text-gray-900">
|
||||
{ticket.prize?.name || 'N/A'}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">
|
||||
Code: {ticket.code}
|
||||
<div className="text-xs text-gray-500 font-mono bg-gray-100 inline-block px-2 py-1 rounded mt-1">
|
||||
{ticket.code}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{ticket.playedAt
|
||||
? new Date(ticket.playedAt).toLocaleDateString('fr-FR')
|
||||
: 'N/A'}
|
||||
<td className="px-6 py-5 whitespace-nowrap">
|
||||
<div className="text-sm font-medium text-gray-700">
|
||||
{ticket.playedAt
|
||||
? new Date(ticket.playedAt).toLocaleDateString('fr-FR')
|
||||
: 'N/A'}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<td className="px-6 py-5 whitespace-nowrap text-right">
|
||||
<button
|
||||
onClick={() => {
|
||||
setSelectedTicket(ticket);
|
||||
setShowModal(true);
|
||||
}}
|
||||
className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 bg-gradient-to-r from-green-600 to-green-700 text-white text-sm font-semibold rounded-lg hover:from-green-700 hover:to-green-800 transition-all shadow-sm hover:shadow-md"
|
||||
>
|
||||
Traiter →
|
||||
Traiter
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -318,14 +343,6 @@ export default function EmployeeVerificationPage() {
|
|||
{selectedTicket.prize?.description || 'Description non disponible'}
|
||||
</p>
|
||||
</div>
|
||||
{selectedTicket.prize?.value && (
|
||||
<div className="mt-2">
|
||||
<p className="text-sm font-medium text-gray-600">Valeur</p>
|
||||
<p className="text-base font-semibold text-gray-900">
|
||||
{selectedTicket.prize.value}€
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{selectedTicket.playedAt && (
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-600">Date de gain</p>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user