// --- FIREBASE INITIALIZATION --- const firebaseConfig = typeof __firebase_config !== 'undefined' ? JSON.parse(__firebase_config) : {}; const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'tiktok-affiliates-app'; export default function App() { const [user, setUser] = useState(null); const [loggedAffiliateId, setLoggedAffiliateId] = useState(null); const isManuallyLoggedOut = useRef(false); const [profile, setProfile] = useState(null); const [leaderboard, setLeaderboard] = useState([]); const [allSubmissions, setAllSubmissions] = useState([]); const [userSubmissions, setUserSubmissions] = useState([]); const [rewardsData, setRewardsData] = useState({ month: '', items: [] }); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState('dashboard'); const [isAdmin, setIsAdmin] = useState(false); const [showAdminModal, setShowAdminModal] = useState(false); const [adminPassword, setAdminPassword] = useState(''); const [adminError, setAdminError] = useState(''); useEffect(() => { const initAuth = async () => { try { if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) { await signInWithCustomToken(auth, __initial_auth_token); } else { await signInAnonymously(auth); } } catch (error) { console.error("Erro na autenticação:", error); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, async (currentUser) => { setUser(currentUser); if (currentUser && !loggedAffiliateId && !isManuallyLoggedOut.current) { try { const docRef = doc(db, 'artifacts', appId, 'public', 'data', 'affiliates', currentUser.uid); const docSnap = await getDoc(docRef); if (docSnap.exists()) { setLoggedAffiliateId(currentUser.uid); } } catch (e) { console.error(e); } } setLoading(false); }); return () => unsubscribe(); }, [loggedAffiliateId]); useEffect(() => { if (!user) return; let unsubProfile = () => {}; if (loggedAffiliateId) { const profileRef = doc(db, 'artifacts', appId, 'public', 'data', 'affiliates', loggedAffiliateId); unsubProfile = onSnapshot(profileRef, (docSnap) => { if (docSnap.exists()) { setProfile(docSnap.data()); } else { setProfile(null); } }); } const affiliatesRef = collection(db, 'artifacts', appId, 'public', 'data', 'affiliates'); const unsubRanking = onSnapshot(affiliatesRef, (snapshot) => { const data = snapshot.docs.map(doc => doc.data()); data.sort((a, b) => b.points - a.points); setLeaderboard(data); }); const submissionsRef = collection(db, 'artifacts', appId, 'public', 'data', 'submissions'); const unsubSubmissions = onSnapshot(submissionsRef, (snapshot) => { const data = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); data.sort((a, b) => b.timestamp - a.timestamp); setAllSubmissions(data); if (loggedAffiliateId) { setUserSubmissions(data.filter(sub => sub.userId === loggedAffiliateId)); } }); const rewardsRef = doc(db, 'artifacts', appId, 'public', 'data', 'settings', 'rewards'); const unsubRewards = onSnapshot(rewardsRef, (docSnap) => { if (docSnap.exists()) { setRewardsData(docSnap.data()); } else { setRewardsData({ month: '', items: [] }); } }); return () => { unsubProfile(); unsubRanking(); unsubSubmissions(); unsubRewards(); }; }, [user, loggedAffiliateId]); const handleLogin = async (tiktokHandle, password) => { const handleClean = tiktokHandle.startsWith('@') ? tiktokHandle : `@${tiktokHandle}`; const snapshot = await getDocs(collection(db, 'artifacts', appId, 'public', 'data', 'affiliates')); const affiliates = snapshot.docs.map(d => ({ id: d.id, ...d.data() })); const foundUser = affiliates.find(a => a.tiktokHandle.toLowerCase() === handleClean.toLowerCase() && a.password === password); if (foundUser) { isManuallyLoggedOut.current = false; setLoggedAffiliateId(foundUser.id); setActiveTab('dashboard'); updateDoc(doc(db, 'artifacts', appId, 'public', 'data', 'affiliates', foundUser.id), { lastActivity: Date.now() }).catch(console.error); } else { throw new Error('Usuário do TikTok ou senha incorretos.'); } }; const handleRegister = async (name, tiktokHandle, email, phone, password) => { const handleClean = tiktokHandle.startsWith('@') ? tiktokHandle : `@${tiktokHandle}`; const snapshot = await getDocs(collection(db, 'artifacts', appId, 'public', 'data', 'affiliates')); const affiliates = snapshot.docs.map(d => d.data()); if (affiliates.some(a => a.tiktokHandle.toLowerCase() === handleClean.toLowerCase())) { throw new Error('Este usuário do TikTok já está cadastrado.'); } const newId = user ? user.uid : crypto.randomUUID(); await setDoc(doc(db, 'artifacts', appId, 'public', 'data', 'affiliates', newId), { id: newId, name, tiktokHandle: handleClean, email, phone, password, points: 0, createdAt: Date.now(), lastActivity: Date.now() }); isManuallyLoggedOut.current = false; setLoggedAffiliateId(newId); setActiveTab('dashboard'); }; const handleLogout = () => { isManuallyLoggedOut.current = true; setLoggedAffiliateId(null); setProfile(null); setIsAdmin(false); }; if (loading) { return (

Afiliados Sapekitos

); } return (

Afiliados Sapekitos

{!loggedAffiliateId && !isAdmin && ( )} {profile && loggedAffiliateId && (

{profile.name}

{profile.points} pts

)} {isAdmin && !loggedAffiliateId && ( )}
{!loggedAffiliateId && !isAdmin ? ( ) : (
{loggedAffiliateId && ( <> )} {isAdmin && ( )}
{activeTab === 'dashboard' && profile ? ( ) : activeTab === 'ranking' && (profile || isAdmin) ? ( ) : isAdmin && activeTab === 'admin' ? ( ) : null}
)}
{(loggedAffiliateId || isAdmin) && ( )} {showAdminModal && (

Acesso da Equipe

Insira a senha do marketing para validar os links.

{ setAdminPassword(e.target.value); setAdminError(''); }} onKeyDown={(e) => { if (e.key === 'Enter') { if (adminPassword === 'sapekitos') { setIsAdmin(true); setActiveTab('admin'); setShowAdminModal(false); setAdminPassword(''); } else { setAdminError('Senha incorreta!'); } } }} className="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 mb-2 text-slate-800 focus:ring-2 focus:ring-indigo-500 focus:border-transparent outline-none transition-all" placeholder="Senha do Marketing" autoFocus /> {adminError &&

{adminError}

}
)}
); } function AuthScreen({ onLogin, onRegister }) { const [isLogin, setIsLogin] = useState(true); const [tiktokHandle, setTiktokHandle] = useState(''); const [password, setPassword] = useState(''); const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [phone, setPhone] = useState(''); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); setError(''); setIsLoading(true); const cleanHandle = tiktokHandle.replace('@', '').trim(); if (!cleanHandle) { setError('O usuário do TikTok é obrigatório.'); setIsLoading(false); return; } if (!isLogin && (!name || !cleanHandle || !email || !phone || !password)) { setError('Todos os campos de cadastro são obrigatórios.'); setIsLoading(false); return; } try { if (isLogin) { await onLogin(cleanHandle, password); } else { await onRegister(name, cleanHandle, email, phone, password); } } catch (err) { setError(err.message); } finally { setIsLoading(false); } }; return (

{isLogin ? 'Bem-vindo(a) de volta!' : 'Crie sua conta'}

{isLogin ? 'Faça login para gerenciar seus links e ver sua pontuação.' : 'Cadastre-se para participar do nosso ranking de afiliados Sapekitos.'}

{error && (
{error}
)} {!isLogin && ( <>
setName(e.target.value)} className="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-slate-800 focus:ring-2 focus:ring-sky-400 focus:border-transparent outline-none transition-all placeholder:text-slate-400" placeholder="Ex: João da Silva" />
setEmail(e.target.value)} className="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-slate-800 focus:ring-2 focus:ring-sky-400 focus:border-transparent outline-none transition-all placeholder:text-slate-400" placeholder="seu@email.com" />
setPhone(e.target.value)} className="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-slate-800 focus:ring-2 focus:ring-sky-400 focus:border-transparent outline-none transition-all placeholder:text-slate-400" placeholder="(11) 99999-9999" />
)}
@ setTiktokHandle(e.target.value.replace('@', ''))} className="w-full pl-9 bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-slate-800 focus:ring-2 focus:ring-sky-400 focus:border-transparent outline-none transition-all placeholder:text-slate-400" placeholder="seu_usuario" />
setPassword(e.target.value)} className="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-slate-800 focus:ring-2 focus:ring-pink-400 focus:border-transparent outline-none transition-all placeholder:text-slate-400" placeholder="******" minLength={6} />
); } function Dashboard({ profile, loggedAffiliateId, submissions, leaderboard }) { const [submissionType, setSubmissionType] = useState('video'); const [url, setUrl] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [successMsg, setSuccessMsg] = useState(''); const [errorMsg, setErrorMsg] = useState(''); const userRankIndex = leaderboard.findIndex(affiliate => affiliate.id === loggedAffiliateId); const userRank = userRankIndex !== -1 ? `${userRankIndex + 1}º` : '-'; const handleSubmitAction = async (e) => { e.preventDefault(); if (!url || !url.includes('tiktok.com')) { setErrorMsg("Por favor, insira um link válido do TikTok."); setTimeout(() => setErrorMsg(''), 5000); return; } setIsSubmitting(true); setSuccessMsg(''); setErrorMsg(''); try { const pointsToAdd = submissionType === 'live' ? 7 : 5; const subRef = collection(db, 'artifacts', appId, 'public', 'data', 'submissions'); await addDoc(subRef, { userId: loggedAffiliateId, userName: profile.name, tiktokHandle: profile.tiktokHandle, type: submissionType, url: url, timestamp: Date.now(), status: 'pending', points: pointsToAdd }); const userRef = doc(db, 'artifacts', appId, 'public', 'data', 'affiliates', loggedAffiliateId); updateDoc(userRef, { lastActivity: Date.now() }).catch(console.error); setUrl(''); setSuccessMsg(`Link enviado para análise! Você receberá os ${pointsToAdd} pontos assim que nossa equipe validar.`); setTimeout(() => setSuccessMsg(''), 6000); } catch (error) { console.error("Erro ao registar ação:", error); setErrorMsg("Houve um erro ao registar. Tente novamente."); setTimeout(() => setErrorMsg(''), 5000); } finally { setIsSubmitting(false); } }; return (

Seus Pontos Aprovados

{profile.points || 0} PTS

Perfil TikTok

{profile.tiktokHandle}

Ranking Global

{userRank}

Registrar Conteúdo

{errorMsg && (
{errorMsg}
)} {successMsg && (
{successMsg}
)}
setUrl(e.target.value)} className="block w-full pl-12 bg-slate-50 border border-slate-200 rounded-xl px-4 py-3.5 text-slate-800 focus:ring-2 focus:ring-sky-400 focus:border-transparent outline-none transition-all placeholder:text-slate-400" placeholder={`Cole aqui o link do seu ${submissionType === 'video' ? 'vídeo' : 'perfil/live'}...`} />

O link será analisado pela equipe de marketing. Após validado, os pontos serão adicionados ao seu perfil.

Entrar no Grupo WhatsApp

Status dos Links

{submissions.length === 0 ? (

Nenhum envio ainda.

Registre seus links ao lado para começar!

) : ( submissions.map((sub) => { let statusConfig = { color: 'text-orange-500', bg: 'bg-orange-100', icon: Clock, text: 'Em Análise' }; if (sub.status === 'approved') statusConfig = { color: 'text-lime-600', bg: 'bg-lime-100', icon: CheckCircle, text: 'Aprovado' }; if (sub.status === 'rejected') statusConfig = { color: 'text-red-500', bg: 'bg-red-100', icon: XCircle, text: 'Rejeitado' }; const StatusIcon = statusConfig.icon; return (
{sub.type === 'video' ?

{sub.type}

{sub.url}

{sub.points} pts
{statusConfig.text}
{sub.status === 'rejected' && sub.rejectionReason && (
Motivo da Rejeição: {sub.rejectionReason}
)}
); }) )}
); } function AdminPanel({ allSubmissions, leaderboard, rewardsData }) { const [processingId, setProcessingId] = useState(null); const [rejectingId, setRejectingId] = useState(null); const [rejectionReason, setRejectionReason] = useState(''); const [rewardsMonth, setRewardsMonth] = useState(rewardsData?.month || ''); const [rewardsList, setRewardsList] = useState(rewardsData?.items || []); const [newRewardText, setNewRewardText] = useState(''); const [savingRewards, setSavingRewards] = useState(false); const [rewardsSuccess, setRewardsSuccess] = useState(false); useEffect(() => { setRewardsMonth(rewardsData?.month || ''); setRewardsList(rewardsData?.items || []); }, [rewardsData]); const pendingSubs = allSubmissions.filter(s => s.status === 'pending'); const handleValidation = async (submission, isApproved, reason = null) => { setProcessingId(submission.id); try { const subRef = doc(db, 'artifacts', appId, 'public', 'data', 'submissions', submission.id); const updateData = { status: isApproved ? 'approved' : 'rejected', reviewedAt: Date.now() }; if (!isApproved && reason) { updateData.rejectionReason = reason; } await updateDoc(subRef, updateData); if (isApproved) { const userProfile = leaderboard.find(u => u.id === submission.userId); if (userProfile) { const userRef = doc(db, 'artifacts', appId, 'public', 'data', 'affiliates', submission.userId); await updateDoc(userRef, { points: (userProfile.points || 0) + submission.points }); } } } catch (error) { console.error("Erro ao validar:", error); alert("Falha ao processar validação."); } finally { setProcessingId(null); if (!isApproved) { setRejectingId(null); setRejectionReason(''); } } }; const handleAddReward = () => { if (newRewardText.trim()) { setRewardsList([...rewardsList, { id: Date.now().toString(), text: newRewardText }]); setNewRewardText(''); } }; const handleRemoveReward = (idToRemove) => { setRewardsList(rewardsList.filter(r => r.id !== idToRemove)); }; const handleSaveRewards = async () => { setSavingRewards(true); setRewardsSuccess(false); try { await setDoc(doc(db, 'artifacts', appId, 'public', 'data', 'settings', 'rewards'), { month: rewardsMonth, items: rewardsList }); setRewardsSuccess(true); setTimeout(() => setRewardsSuccess(false), 3000); } catch (e) { console.error("Erro ao salvar premiações", e); } finally { setSavingRewards(false); } }; return (

Painel do Marketing

Avalie os links e configure as premiações para os afiliados.

Configurar Premiações do Mês

setRewardsMonth(e.target.value)} placeholder="Ex: Março/2026" className="w-full bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-slate-800 focus:ring-2 focus:ring-pink-400 outline-none transition-all" />
setNewRewardText(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleAddReward()} placeholder="Ex: 1º Lugar - Kit Completo + R$ 500" className="flex-1 bg-slate-50 border border-slate-200 rounded-xl px-4 py-3 text-slate-800 focus:ring-2 focus:ring-pink-400 outline-none transition-all" />
{rewardsList.length > 0 && (

Prêmios Atuais

{rewardsList.map((reward) => (
{reward.text}
))}
)}
{rewardsSuccess ? ( Atualizado com sucesso! ) : ( Salve para atualizar o ranking global. )}

Fila de Análise

{pendingSubs.length} pendentes
{pendingSubs.length === 0 ? (
Oba! Nenhum link pendente de aprovação no momento.
) : ( pendingSubs.map((sub) => (
{sub.userName} {sub.tiktokHandle}
{sub.type === 'video' ? Vale {sub.points} pts
{sub.url}
{rejectingId !== sub.id && ( <> )}
{rejectingId === sub.id && (