import React, { useState, useEffect } from 'react';
import { initializeApp } from 'firebase/app';
import {
getAuth,
signInWithCustomToken,
signInAnonymously,
GoogleAuthProvider,
signInWithPopup,
signOut,
onAuthStateChanged
} from 'firebase/auth';
import {
getFirestore,
collection,
query,
onSnapshot,
addDoc,
serverTimestamp,
getDocs,
where
} from 'firebase/firestore';
import { Star, Send, LogIn, LogOut, ShieldCheck, AlertCircle, Loader2 } from 'lucide-react';
// ==========================================
// CONFIGURASI FIREBASE
// Tempelkan konfigurasi Firebase Anda di sini
// ==========================================
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 : 'blogger-comments-v1';
const apiKey = ""; // API Key untuk Gemini AI (otomatis terisi di runtime)
const App = () => {
const [user, setUser] = useState(null);
const [comments, setComments] = useState([]);
const [newComment, setNewComment] = useState("");
const [rating, setRating] = useState(5);
const [hover, setHover] = useState(0);
const [loading, setLoading] = useState(false);
const [hasCommented, setHasCommented] = useState(false);
const [error, setError] = useState(null);
// 1. Initial Auth & User State
useEffect(() => {
const initAuth = async () => {
if (typeof __initial_auth_token !== 'undefined' && __initial_auth_token) {
await signInWithCustomToken(auth, __initial_auth_token);
}
};
initAuth();
const unsubscribe = onAuthStateChanged(auth, (u) => {
setUser(u);
if (u) checkUserCommentStatus(u.uid);
});
return () => unsubscribe();
}, []);
// 2. Real-time Comments Fetching
useEffect(() => {
if (!user) return;
const q = collection(db, 'artifacts', appId, 'public', 'data', 'comments');
const unsubscribe = onSnapshot(q, (snapshot) => {
const docs = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }));
// Sort manual (Rule 2: No complex queries/orderBy)
const sorted = docs.sort((a, b) => b.createdAt?.seconds - a.createdAt?.seconds);
setComments(sorted);
}, (err) => {
console.error("Firestore Error:", err);
});
return () => unsubscribe();
}, [user]);
const checkUserCommentStatus = async (uid) => {
const q = collection(db, 'artifacts', appId, 'public', 'data', 'comments');
const querySnapshot = await getDocs(q);
const alreadyCommented = querySnapshot.docs.some(doc => doc.data().userId === uid);
setHasCommented(alreadyCommented);
};
const handleGoogleLogin = async () => {
const provider = new GoogleAuthProvider();
try {
await signInWithPopup(auth, provider);
} catch (err) {
setError("Gagal login dengan Google.");
}
};
const handleLogout = () => signOut(auth);
// 3. AI Moderation via Gemini
const moderateWithAI = async (text) => {
try {
const prompt = `Lakukan moderasi konten untuk komentar blog berikut: "${text}".
Berikan respon JSON murni dengan format: {"isSafe": boolean, "reason": "string"}.
isSafe false jika mengandung kata kasar, spam, sara, atau ujaran kebencian dalam bahasa Indonesia.`;
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
contents: [{ parts: [{ text: prompt }] }],
generationConfig: { responseMimeType: "application/json" }
})
});
const result = await response.json();
return JSON.parse(result.candidates[0].content.parts[0].text);
} catch (err) {
console.error("AI Moderation Error", err);
return { isSafe: true }; // Fallback jika AI error
}
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!newComment.trim() || hasCommented) return;
setLoading(true);
setError(null);
// AI Check
const moderation = await moderateWithAI(newComment);
if (!moderation.isSafe) {
setError(`Komentar ditolak: ${moderation.reason}`);
setLoading(false);
return;
}
try {
await addDoc(collection(db, 'artifacts', appId, 'public', 'data', 'comments'), {
userId: user.uid,
userName: user.displayName,
userPhoto: user.photoURL,
text: newComment,
rating: rating,
createdAt: serverTimestamp()
});
setNewComment("");
setHasCommented(true);
} catch (err) {
setError("Gagal mengirim komentar.");
} finally {
setLoading(false);
}
};
return (
) : (
);
};
export default App;
Komentar & Rating
{/* Auth Section */} {!user ? (Login Diperlukan
Silakan login dengan akun Gmail Anda untuk memberikan rating dan komentar.
{/* Form Komentar */}
{!hasCommented ? (
) : (
{item.createdAt?.seconds ? new Date(item.createdAt.seconds * 1000).toLocaleDateString() : 'Baru saja'}
))
)}
)}
Terima kasih! Anda telah memberikan komentar dan rating.
)}
{/* List Komentar */}
Komentar Terbaru ({comments.length})
{comments.length === 0 ? (
Belum ada komentar. Jadilah yang pertama!
) : (
comments.map((item) => (
{item.userName}
{[...Array(5)].map((_, i) => (
))}
{item.text}