SEO
import React, { useState, useEffect } from 'react'; import { initializeApp } from 'firebase/app'; import { getFirestore, collection, addDoc, onSnapshot, query, orderBy, timestamp, serverTimestamp } from 'firebase/firestore'; import { getAuth, signInAnonymously, onAuthStateChanged, signInWithCustomToken } from 'firebase/auth'; import { Globe, PlusCircle, Link as LinkIcon, FileText, ChevronLeft, ChevronRight, Search } from 'lucide-react'; // Firebase Configuration const firebaseConfig = JSON.parse(__firebase_config); const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = typeof __app_id !== 'undefined' ? __app_id : 'seo-directory-app'; export default function App() { const [user, setUser] = useState(null); const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const [formData, setFormData] = useState({ name: '', link: '', description: '' }); const [currentPage, setCurrentPage] = useState(1); const postsPerPage = 15; // 1. Authentication logic (Rule 3) 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("Auth error:", error); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, (user) => { setUser(user); }); return () => unsubscribe(); }, []); // 2. Data Fetching (Rule 1 & 2) useEffect(() => { if (!user) return; const postsRef = collection(db, 'artifacts', appId, 'public', 'data', 'seo_posts'); // Simple query (Rule 2: no complex filtering) const q = query(postsRef); const unsubscribe = onSnapshot(q, (snapshot) => { const fetchedPosts = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); // Sort in memory as per Rule 2 const sortedPosts = fetchedPosts.sort((a, b) => { const timeA = a.createdAt?.seconds || 0; const timeB = b.createdAt?.seconds || 0; return timeB - timeA; }); setPosts(sortedPosts); setLoading(false); }, (error) => { console.error("Firestore error:", error); setLoading(false); }); return () => unsubscribe(); }, [user]); const handleSubmit = async (e) => { e.preventDefault(); if (!user || !formData.name || !formData.link) return; try { const postsRef = collection(db, 'artifacts', appId, 'public', 'data', 'seo_posts'); await addDoc(postsRef, { name: formData.name, link: formData.link.startsWith('http') ? formData.link : `https://${formData.link}`, description: formData.description, createdAt: serverTimestamp(), userId: user.uid }); setFormData({ name: '', link: '', description: '' }); } catch (error) { console.error("Error adding post:", error); } }; // Pagination Logic const indexOfLastPost = currentPage * postsPerPage; const indexOfFirstPost = indexOfLastPost - postsPerPage; const currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost); const totalPages = Math.ceil(posts.length / postsPerPage); if (loading) { return (
{/* Header */}
{/* Form Section */}
{/* List Section */}
{/* Footer info for user */}
);
} SEO Directory
DB: Connected
Tambah Website Baru
Daftar Website ({posts.length})
{currentPosts.length > 0 ? (
currentPosts.map((post) => (
))
) : (
)}
{/* Pagination Controls */}
{totalPages > 1 && (
ID: {post.id.substring(0,6)}
Belum ada data. Silakan input website pertama Anda!
Halaman {currentPage} dari {totalPages}
)}