139 lines
7.5 KiB
TypeScript
139 lines
7.5 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { motion } from 'framer-motion';
|
|
import DashboardLayout from '@/components/layout/DashboardLayout';
|
|
import { api } from '@/lib/api';
|
|
import { useAuth } from '@/lib/auth';
|
|
|
|
export default function BenefitsPage() {
|
|
const { user } = useAuth();
|
|
const [benefits, setBenefits] = useState<any[]>([]);
|
|
const [categories, setCategories] = useState<any[]>([]);
|
|
const [search, setSearch] = useState('');
|
|
const [selectedCategory, setSelectedCategory] = useState('');
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
api.getCategories().then(d => setCategories(d.categories || [])).catch(console.error);
|
|
loadBenefits();
|
|
}, []);
|
|
|
|
const loadBenefits = async (catId?: string) => {
|
|
setLoading(true);
|
|
try {
|
|
const params = new URLSearchParams();
|
|
if (catId) params.set('category', catId);
|
|
if (search) params.set('search', search);
|
|
const d = await api.getBenefits(params.toString());
|
|
setBenefits(d.benefits || []);
|
|
} catch (e) { console.error(e); }
|
|
setLoading(false);
|
|
};
|
|
|
|
const handleFilter = (catId: string) => {
|
|
setSelectedCategory(catId);
|
|
loadBenefits(catId);
|
|
};
|
|
|
|
const handleRedeem = async (id: number) => {
|
|
try {
|
|
await api.redeemBenefit(id);
|
|
alert('Benefício resgatado com sucesso! Verifique seu e-mail.');
|
|
} catch (err: any) {
|
|
alert(err.message || 'Erro ao resgatar');
|
|
}
|
|
};
|
|
|
|
const handleFavorite = async (id: number) => {
|
|
try {
|
|
await api.addFavorite(id);
|
|
alert('Adicionado aos favoritos!');
|
|
} catch { }
|
|
};
|
|
|
|
return (
|
|
<DashboardLayout>
|
|
<div className="space-y-8">
|
|
<div>
|
|
<h1 className="text-3xl font-bold">🎁 <span className="gradient-text">Benefícios</span></h1>
|
|
<p className="text-dark-400 mt-1">Explore e resgate benefícios exclusivos dos nossos parceiros.</p>
|
|
</div>
|
|
|
|
{/* Search + Filter */}
|
|
<div className="flex flex-col md:flex-row gap-4">
|
|
<div className="flex-1">
|
|
<input type="text" value={search} onChange={e => setSearch(e.target.value)}
|
|
onKeyDown={e => e.key === 'Enter' && loadBenefits(selectedCategory)}
|
|
placeholder="Buscar benefícios..."
|
|
className="w-full px-4 py-3 bg-dark-900 border border-dark-700 rounded-xl text-white focus:border-brand-500 outline-none" />
|
|
</div>
|
|
<div className="flex gap-2 overflow-x-auto pb-2">
|
|
<button onClick={() => handleFilter('')}
|
|
className={`px-4 py-2 rounded-xl text-sm whitespace-nowrap transition-all ${!selectedCategory ? 'bg-brand-600 text-white' : 'glass text-dark-400 hover:text-white'
|
|
}`}>Todos</button>
|
|
{categories.map(c => (
|
|
<button key={c.id} onClick={() => handleFilter(c.id.toString())}
|
|
className={`px-4 py-2 rounded-xl text-sm whitespace-nowrap transition-all ${selectedCategory === c.id.toString() ? 'bg-brand-600 text-white' : 'glass text-dark-400 hover:text-white'
|
|
}`}>
|
|
{c.icon} {c.name}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Benefits Grid */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
|
|
{loading ? [1, 2, 3, 4, 5, 6].map(i => <div key={i} className="skeleton h-56 rounded-2xl" />) :
|
|
benefits.length === 0 ? (
|
|
<div className="col-span-full text-center py-16 glass rounded-2xl">
|
|
<div className="text-5xl mb-4">🔍</div>
|
|
<p className="text-xl font-semibold mb-2">Nenhum benefício encontrado</p>
|
|
<p className="text-dark-400">Tente ajustar os filtros de busca.</p>
|
|
</div>
|
|
) :
|
|
benefits.map((b, i) => (
|
|
<motion.div key={b.id} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }}
|
|
transition={{ delay: i * 0.05 }}
|
|
className="glass rounded-2xl overflow-hidden hover:bg-white/5 transition-all group">
|
|
{/* Gradient Header */}
|
|
<div className="h-2" style={{ background: `linear-gradient(to right, ${b.gradient_from || '#3B82F6'}, ${b.gradient_to || '#8B5CF6'})` }} />
|
|
<div className="p-5">
|
|
<div className="flex items-start justify-between mb-3">
|
|
<div>
|
|
<h3 className="font-bold text-lg group-hover:text-brand-400 transition-colors">{b.title}</h3>
|
|
<p className="text-xs text-dark-500 mt-1">{b.partner_name || 'Parceiro'}</p>
|
|
</div>
|
|
{b.discount_percent && (
|
|
<div className="bg-green-500/15 text-green-400 text-sm font-bold px-3 py-1 rounded-lg">
|
|
-{b.discount_percent}%
|
|
</div>
|
|
)}
|
|
</div>
|
|
<p className="text-sm text-dark-400 mb-4 line-clamp-2">{b.description}</p>
|
|
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
{b.badge && <span className="text-xs px-2 py-1 rounded-lg bg-purple-500/15 text-purple-400">{b.badge}</span>}
|
|
{b.is_global && <span className="text-xs px-2 py-1 rounded-lg bg-brand-500/15 text-brand-400">🌐 Global</span>}
|
|
</div>
|
|
<div className="flex gap-2">
|
|
<button onClick={() => handleFavorite(b.id)}
|
|
className="p-2 rounded-lg hover:bg-yellow-500/15 text-dark-400 hover:text-yellow-400 transition-all" title="Favoritar">
|
|
⭐
|
|
</button>
|
|
<button onClick={() => handleRedeem(b.id)}
|
|
className="px-4 py-2 bg-brand-600 hover:bg-brand-500 rounded-lg text-sm font-semibold transition-all">
|
|
Resgatar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</DashboardLayout>
|
|
);
|
|
}
|