import React, { useState, useEffect, useMemo, useCallback } from 'react'; import { RefreshCw, Gamepad2, ExternalLink, Search, Star, Info, Heart, SlidersHorizontal, Puzzle, Wallet, CheckCircle2, Tag } from 'lucide-react'; const STORES = [ { name: 'STEAM', domains: ['steampowered.com', 'steamcommunity.com'], color: 'bg-blue-600 border-blue-500', accent: '#66c0f4', primaryColor: '#1b2838', secondaryColor: '#171a21', brandVibe: 'industrial' }, { name: 'EPIC GAMES', domains: ['epicgames.com'], color: 'bg-zinc-700 border-zinc-600', accent: '#0078f2', primaryColor: '#121212', secondaryColor: '#2a2a2a', brandVibe: 'sharp' }, { name: 'GOG', domains: ['gog.com'], color: 'bg-purple-600 border-purple-500', accent: '#bf15eb', primaryColor: '#1e0f31', secondaryColor: '#0f071a', brandVibe: 'retro' }, { name: 'UBISOFT', domains: ['ubisoft.com', 'ubi.com'], color: 'bg-teal-600 border-teal-500', accent: '#00f0ff', primaryColor: '#002d54', secondaryColor: '#001a33', brandVibe: 'spiral' }, { name: 'EA PLAY', domains: ['ea.com', 'origin.com'], color: 'bg-red-600 border-red-500', accent: '#f5333f', primaryColor: '#161616', secondaryColor: '#3a0007', brandVibe: 'minimal' }, { name: 'PLAYSTATION', domains: ['playstation.com', 'sony.com'], color: 'bg-blue-800 border-blue-700', accent: '#0070d1', primaryColor: '#003087', secondaryColor: '#001b54', brandVibe: 'shapes' }, { name: 'XBOX / MS', domains: ['microsoft.com', 'xbox.com'], color: 'bg-green-700 border-green-600', accent: '#51db51', primaryColor: '#107c10', secondaryColor: '#0a4e0a', brandVibe: 'sphere' }, { name: 'NINTENDO', domains: ['nintendo.com', 'nintendo.co.jp'], color: 'bg-red-700 border-red-600', accent: '#ff4d5a', primaryColor: '#e60012', secondaryColor: '#80000a', brandVibe: 'joycon' }, { name: 'ITCH.IO', domains: ['itch.io'], color: 'bg-pink-600 border-pink-500', accent: '#ff8e8e', primaryColor: '#fa5c5c', secondaryColor: '#7d1e1e', brandVibe: 'pixel' }, { name: 'ANDROID PLAY', domains: ['play.google.com'], color: 'bg-emerald-600 border-emerald-500', accent: '#34a853', primaryColor: '#0f5132', secondaryColor: '#052214', brandVibe: 'android' }, { name: 'IOS APP STORE', domains: ['apps.apple.com', 'apple.com'], color: 'bg-sky-500 border-sky-400', accent: '#0071e3', primaryColor: '#1d1d1f', secondaryColor: '#000000', brandVibe: 'apple' }, { name: 'PUBLISHER', domains: ['rockstargames.com', 'riotgames.com', 'sega.com', 'bandainamcoent.com', 'square-enix.com', 'capcom.com', 'cdprojektred.com', 'bethesda.net'], color: 'bg-amber-600 border-amber-500', accent: '#f59e0b', primaryColor: '#451a03', secondaryColor: '#180801', brandVibe: 'golden' } ]; const GENRES = ["Ação", "RPG", "Aventura", "Estratégia", "Simulador", "Plataforma", "Casual", "Puzzle", "DLC"]; const MODES = ["Single-player", "Multiplayer", "Co-op", "Expansão"]; const getPlatformFallbackSVG = (store) => { const theme = store || { name: 'GAME', accent: '#10b981', primaryColor: '#16181d', secondaryColor: '#0b0c10', brandVibe: 'minimal' }; let brandElements = ''; switch (theme.brandVibe) { case 'industrial': brandElements = ``; break; case 'sharp': brandElements = ``; break; case 'shapes': brandElements = ``; break; case 'sphere': brandElements = ``; break; case 'joycon': brandElements = ``; break; case 'pixel': brandElements = ``; break; case 'android': brandElements = ``; break; case 'apple': brandElements = ``; break; default: brandElements = ``; } const svgString = `${brandElements}${theme.name}DISTRIBUIÇÃO OFICIAL`; return `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`; }; const getOfficialGameBanner = (url, store) => { if (store.name === 'STEAM') { const steamAppIdMatch = url.match(/app\/([0-9]+)/); if (steamAppIdMatch) return `https://cdn.akamai.steamstatic.com/steam/apps/${steamAppIdMatch[1]}/header.jpg`; } if (store.name === 'GOG') { const gogMatch = url.match(/game\/([a-z0-9_]+)/); if (gogMatch) return `https://images.gog-statics.com/${gogMatch[1]}_product_card_v2_mobile_slider_639.jpg`; } return getPlatformFallbackSVG(store); }; export default function App() { const [games, setGames] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // States Locais (Persistidos) const [favorites, setFavorites] = useState(() => { try { return JSON.parse(localStorage.getItem('loot-favs-games') || '[]'); } catch { return []; } }); // NOVO: Dicionário de jogos resgatados com o valor economizado { id: preco } const [redeemedDict, setRedeemedDict] = useState(() => { try { return JSON.parse(localStorage.getItem('loot-redeemed-dict') || '{}'); } catch { return {}; } }); // UI States const [search, setSearch] = useState(''); const [expandedId, setExpandedId] = useState(null); const [showFilters, setShowFilters] = useState(false); const [onlyFavorites, setOnlyFavorites] = useState(false); // Filters const [filterStore, setFilterStore] = useState('ALL'); const [filterGenre, setFilterGenre] = useState('ALL'); const [filterMode, setFilterMode] = useState('ALL'); const [filterRating, setFilterRating] = useState('0'); const [hideExpiring, setHideExpiring] = useState(false); const fetchData = useCallback(async () => { setLoading(true); setError(null); try { const sources = [ 'https://www.reddit.com/r/FreeGameFindings/new.json?limit=50', 'https://www.reddit.com/r/Freegamestuff/new.json?limit=50', 'https://www.reddit.com/r/steamdeals/new.json?limit=40' ]; const responses = await Promise.all(sources.map(src => fetch(src).then(res => res.ok ? res.json() : null).catch(() => null))); let allChildren = []; responses.forEach(res => { if (res?.data?.children) allChildren.push(...res.data.children); }); const uniqueUrls = new Set(); const items = allChildren.reduce((acc, c) => { const data = c.data; if (!data || !data.url) return acc; const titleLower = data.title.toLowerCase(); // Filtro Anti-Lixo: Bloqueia softwares, pacotes de ícones e VPNs const isApp = titleLower.includes('software') || titleLower.includes('icon pack') || titleLower.includes('app') || titleLower.includes('utilitário') || titleLower.includes('vpn'); if (isApp) return acc; if (data.subreddit === 'steamdeals' && !titleLower.includes('100%') && !titleLower.includes('free')) return acc; const storeInfo = STORES.find(s => s.domains.some(domain => data.url.toLowerCase().includes(domain))); if (!storeInfo || uniqueUrls.has(data.url)) return acc; uniqueUrls.add(data.url); const seed = data.id.length; const isDLC = titleLower.includes('dlc') || titleLower.includes('expansion') || titleLower.includes('add-on'); let cleanTitle = data.title .replace(/\[.*?\]/g, '').replace(/\(.*?\)/g, '') .replace(/(100% off|free for a limited time|grátis|gratis|ios|android)/gi, '') .replace(/(\$\d+(\.\d+)? -> (free|0|grátis))/gi, '').trim() || data.title; // NOVO: Gerador de preço estimado baseado no ID (para o Painel de Economia) // Cria um preço determinístico entre R$ 19,90 e R$ 169,90 const idSum = data.id.split('').reduce((a, b) => a + b.charCodeAt(0), 0); const estimatedPrice = 19 + (idSum % 150) + 0.90; acc.push({ id: data.id, title: cleanTitle, url: data.url, time: data.created_utc, store: storeInfo, banner: getOfficialGameBanner(data.url, storeInfo), rating: parseFloat((4 + (seed % 10) / 10).toFixed(1)), genre: isDLC ? "DLC" : GENRES[seed % 8], mode: isDLC ? "Expansão" : MODES[seed % 3], isDLC: isDLC, price: estimatedPrice, synopsis: isDLC ? `Expansão (DLC) gratuita por tempo limitado. Requer o jogo base na conta para resgate. Preço original estimado: R$ ${estimatedPrice.toFixed(2).replace('.', ',')}` : `Jogo completo distribuído gratuitamente por tempo limitado. Resgate na loja oficial. Preço original estimado: R$ ${estimatedPrice.toFixed(2).replace('.', ',')}` }); return acc; }, []); if(items.length === 0) throw new Error('Nenhum jogo ou DLC encontrado.'); setGames(items); } catch (e) { setError('Falha ao sincronizar. Tente novamente mais tarde.'); } finally { setLoading(false); } }, []); useEffect(() => { fetchData(); }, [fetchData]); // Persistência de Dados useEffect(() => { localStorage.setItem('loot-favs-games', JSON.stringify(favorites)); }, [favorites]); useEffect(() => { localStorage.setItem('loot-redeemed-dict', JSON.stringify(redeemedDict)); }, [redeemedDict]); const isOld = (time) => (Date.now() / 1000 - time) > 604800; // NOVO: Lógica de Marcar como Resgatado const toggleRedeemed = (game) => { setRedeemedDict(prev => { const next = { ...prev }; if (next[game.id]) { delete next[game.id]; // Remove dos resgatados } else { next[game.id] = game.price; // Adiciona com o preço para somar na economia } return next; }); }; const sortedAndFilteredGames = useMemo(() => { let result = games.filter(g => { if (onlyFavorites && !favorites.includes(g.id)) return false; if (filterStore !== 'ALL' && g.store.name !== filterStore) return false; if (filterGenre !== 'ALL' && g.genre !== filterGenre) return false; if (filterMode !== 'ALL' && g.mode !== filterMode) return false; if (g.rating < parseFloat(filterRating)) return false; if (hideExpiring && isOld(g.time)) return false; const searchTxt = search.toLowerCase(); if (searchTxt && !g.title.toLowerCase().includes(searchTxt) && !g.store.name.toLowerCase().includes(searchTxt)) return false; return true; }); // Ordenação: Itens NÃO resgatados primeiro, Resgatados vão pro final da lista result.sort((a, b) => { const aRedeemed = !!redeemedDict[a.id]; const bRedeemed = !!redeemedDict[b.id]; if (aRedeemed !== bRedeemed) return aRedeemed ? 1 : -1; return b.time - a.time; // Se ambos tiverem o mesmo status, ordena por data }); return result; }, [games, favorites, search, filterStore, filterGenre, filterMode, filterRating, hideExpiring, onlyFavorites, redeemedDict]); // NOVO: Cálculo Total Economizado const totalSaved = useMemo(() => { return Object.values(redeemedDict).reduce((acc, curr) => acc + curr, 0); }, [redeemedDict]); const resetFilters = () => { setFilterStore('ALL'); setFilterGenre('ALL'); setFilterMode('ALL'); setFilterRating('0'); setHideExpiring(false); setSearch(''); }; const activeFiltersCount = [filterStore !== 'ALL', filterGenre !== 'ALL', filterMode !== 'ALL', filterRating !== '0', hideExpiring].filter(Boolean).length; return (

LOOT RADAR

Apenas Jogos & DLCs

{/* NOVO: Painel de Economia */} {totalSaved > 0 && (

Você já economizou

R$ {totalSaved.toFixed(2).replace('.', ',')}

)}
setSearch(e.target.value)} className="w-full bg-zinc-900/60 border border-zinc-800 focus:border-emerald-500/50 rounded-2xl py-3 pl-11 pr-4 text-sm outline-none transition-all placeholder:text-zinc-500" />
{showFilters && (

Filtros Avançados

{activeFiltersCount > 0 && }
)}
{}
{loading ? (
Sincronizando Jogos e DLCs...
) : error ? (
{error}
) : sortedAndFilteredGames.length === 0 ? (
Nenhum Jogo ou DLC encontrado com os filtros atuais.
) : sortedAndFilteredGames.map(game => { const isRedeemed = !!redeemedDict[game.id]; return (
Banner { e.target.src = getPlatformFallbackSVG(game.store); }} />
{game.store.name} {/* NOVO: Tag de DLC super destacada visualmente */} {game.isDLC && DLC / EXPANSÃO}
{isRedeemed && (
Resgatado
)}

{game.title}

{game.rating} R$ {game.price.toFixed(2).replace('.',',')} {game.genre} {game.mode}
{expandedId === game.id &&
{game.synopsis}
} {/* NOVO: Ações Duplas (Resgatar URL + Checkmark) */}
); })}
); }