<!doctype html>
<html lang="id">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Citizen Journalism Portal</title>
<style>
body { font-family: system-ui, sans-serif; background: #081020; color: #e6eef6; margin: 0; padding: 24px; }
h1, h2 { color: #60a5fa; }
.container { max-width: 960px; margin: auto; }
.btn { background: #2563eb; color: white; border: none; padding: 8px 14px; border-radius: 6px; cursor: pointer; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
input, textarea { width: 100%; padding: 8px; margin: 6px 0; border-radius: 6px; border: 1px solid #334155; background: #0f172a; color: #e6eef6; }
.grid { display: grid; gap: 12px; }
.author-card { display: flex; align-items: center; gap: 12px; background: rgba(255,255,255,0.05); border-radius: 12px; padding: 12px; }
.author-photo { width: 60px; height: 60px; border-radius: 50%; object-fit: cover; }
.author-info { flex: 1; }
.rank { font-weight: bold; color: #38bdf8; font-size: 20px; width: 32px; text-align: center; }
.form-section { background: rgba(255,255,255,0.05); padding: 16px; border-radius: 12px; margin-top: 24px; }
</style>
</head>
<body>
<div class="container">
<h1>📢 Portal Citizen Journalism</h1>
<div id="auth-section" class="grid">
<h2>Masuk / Daftar</h2>
<input id="email" type="email" placeholder="Email" />
<input id="password" type="password" placeholder="Password" />
<button id="signup-btn" class="btn">Daftar</button>
<button id="signin-btn" class="btn">Masuk</button>
<button id="google-btn" class="btn">Masuk dengan Google</button>
<button id="reset-btn" class="btn">Lupa Password</button>
</div>
<div id="dashboard" style="display:none">
<h2>Selamat datang, <span id="user-name"></span>!</h2>
<button id="logout-btn" class="btn">Keluar</button>
<hr>
<h2>🏆 Peringkat Penulis</h2>
<div id="leaderboard" class="grid"></div>
<div class="form-section">
<h2>🧑🎨 Profil Saya</h2>
<form id="profile-form">
<label>Nama Lengkap</label>
<input type="text" id="profile-name" required />
<label>Bio Singkat</label>
<textarea id="profile-bio" required></textarea>
<label>Instagram</label>
<input type="url" id="profile-ig" placeholder="https://instagram.com/..." />
<label>Facebook</label>
<input type="url" id="profile-fb" placeholder="https://facebook.com/..." />
<label>YouTube</label>
<input type="url" id="profile-yt" placeholder="https://youtube.com/@..." />
<label>X (Twitter)</label>
<input type="url" id="profile-x" placeholder="https://x.com/..." />
<label>Foto Profil</label>
<input type="file" id="profile-photo" accept="image/png,image/jpeg,image/jpg" required />
<small>📸 Maksimum ukuran 2 MB (format: JPG, JPEG, PNG)</small>
<img id="photo-preview" src="" alt="Preview" style="display:none; margin-top:10px; width:100px; height:100px; border-radius:50%; object-fit:cover;">
<button type="submit" id="save-profile" class="btn" disabled>Simpan Profil</button>
</form>
</div>
</div>
</div>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-app.js";
import { getAuth, createUserWithEmailAndPassword, signInWithEmailAndPassword, GoogleAuthProvider, signInWithPopup, sendPasswordResetEmail, signOut, onAuthStateChanged } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-auth.js";
import { getFirestore, doc, setDoc, getDoc, collection, getDocs } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-firestore.js";
import { getStorage, ref, uploadBytes, getDownloadURL } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-storage.js";
const firebaseConfig = {
apiKey: "API_KEY_KAMU",
authDomain: "PROJECT_ID.firebaseapp.com",
projectId: "PROJECT_ID",
storageBucket: "PROJECT_ID.appspot.com",
messagingSenderId: "SENDER_ID",
appId: "APP_ID"
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);
const storage = getStorage(app);
const emailEl = document.getElementById('email');
const passEl = document.getElementById('password');
const authSection = document.getElementById('auth-section');
const dashboard = document.getElementById('dashboard');
const userNameEl = document.getElementById('user-name');
const leaderboardEl = document.getElementById('leaderboard');
document.getElementById('signup-btn').onclick = async () => {
const email = emailEl.value;
const pass = passEl.value;
await createUserWithEmailAndPassword(auth, email, pass);
};
document.getElementById('signin-btn').onclick = async () => {
const email = emailEl.value;
const pass = passEl.value;
await signInWithEmailAndPassword(auth, email, pass);
};
document.getElementById('google-btn').onclick = async () => {
const provider = new GoogleAuthProvider();
await signInWithPopup(auth, provider);
};
document.getElementById('reset-btn').onclick = async () => {
const email = emailEl.value;
await sendPasswordResetEmail(auth, email);
alert('Link reset password dikirim ke email.');
};
document.getElementById('logout-btn').onclick = () => signOut(auth);
onAuthStateChanged(auth, async (user) => {
if (user) {
authSection.style.display = 'none';
dashboard.style.display = 'block';
userNameEl.textContent = user.email;
loadAuthors();
loadProfile(user.uid);
} else {
authSection.style.display = 'grid';
dashboard.style.display = 'none';
}
});
function hitungSkor(a){ return (a.artikel||0)*10 + (a.unduhan||0)/10 + (a.rating||0)*20; }
async function loadAuthors(){
const snapshot = await getDocs(collection(db, 'authors'));
const authors = snapshot.docs.map(d => d.data());
authors.sort((a,b)=>hitungSkor(b)-hitungSkor(a));
leaderboardEl.innerHTML = authors.map((a,i)=>`
<div class="author-card">
<div class="rank">#${i+1}</div>
<img src="${a.foto || 'https://via.placeholder.com/60'}" class="author-photo">
<div class="author-info">
<div><strong>${a.nama}</strong></div>
<div>${a.bio||''}</div>
</div>
</div>
`).join('');
}
const profileForm = document.getElementById('profile-form');
const saveBtn = document.getElementById('save-profile');
const photoInput = document.getElementById('profile-photo');
const photoPreview = document.getElementById('photo-preview');
let uploadedPhotoURL = '';
photoInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
const validTypes = ['image/jpeg', 'image/png', 'image/jpg'];
if (!validTypes.includes(file.type)) {
alert('Hanya file JPG, JPEG, atau PNG yang diperbolehkan.');
e.target.value = '';
saveBtn.disabled = true;
return;
}
if (file.size > 2 * 1024 * 1024) {
alert('Ukuran file maksimal 2 MB.');
e.target.value = '';
saveBtn.disabled = true;
return;
}
const user = auth.currentUser;
const storageRef = ref(storage, `avatars/${user.uid}.jpg`);
await uploadBytes(storageRef, file);
uploadedPhotoURL = await getDownloadURL(storageRef);
photoPreview.src = uploadedPhotoURL;
photoPreview.style.display = 'block';
saveBtn.disabled = false;
});
async function loadProfile(uid){
const docRef = doc(db, 'authors', uid);
const docSnap = await getDoc(docRef);
if (docSnap.exists()){
const d = docSnap.data();
document.getElementById('profile-name').value = d.nama || '';
document.getElementById('profile-bio').value = d.bio || '';
document.getElementById('profile-ig').value = d.sosial?.instagram || '';
document.getElementById('profile-fb').value = d.sosial?.facebook || '';
document.getElementById('profile-yt').value = d.sosial?.youtube || '';
document.getElementById('profile-x').value = d.sosial?.x || '';
if (d.foto){
photoPreview.src = d.foto;
photoPreview.style.display = 'block';
uploadedPhotoURL = d.foto;
saveBtn.disabled = false;
}
}
}
profileForm.addEventListener('submit', async (e) => {
e.preventDefault();
if (!uploadedPhotoURL){ alert('Silakan upload foto profil terlebih dahulu.'); return; }
const user = auth.currentUser;
const data = {
nama: document.getElementById('profile-name').value,
bio: document.getElementById('profile-bio').value,
foto: uploadedPhotoURL,
sosial: {
instagram: document.getElementById('profile-ig').value,
facebook: document.getElementById('profile-fb').value,
youtube: document.getElementById('profile-yt').value,
x: document.getElementById('profile-x').value
}
};
await setDoc(doc(db, 'authors', user.uid), data, { merge: true });
alert('Profil berhasil disimpan!');
loadAuthors();
});
</script>
</body>
</html>