import React, { useState, useEffect } from 'react'; import { initializeApp } from 'firebase/app'; import { getFirestore, collection, doc, setDoc, onSnapshot, } from 'firebase/firestore'; import { getAuth, signInAnonymously, onAuthStateChanged } from 'firebase/auth'; import { Store, User, MapPin, Plus, Search, CheckCircle, ChevronLeft, Map as MapIcon, Info, Camera, Navigation, Loader2, LogIn, Save, Trash2, AlertCircle, ExternalLink, ChevronDown } from 'lucide-react'; // --- VARIABEL GLOBAL & KATEGORI BARU --- const APP_TITLE = "KATALOG KELAS PEKERJA"; const APP_URL = "katalog.spiritbinokasih.my.id"; const CATEGORY_GROUPS = { "Bidang Kuliner": [ "Katering rumahan atau warmindo", "Minuman kekinian (teh unik, healthy drinks)", "Camilan dari bahan lokal atau jajanan pasar", "Usaha makanan ringan atau berat lainnya" ], "Bidang Fashion": [ "Online shop baju (reseller, dropshipper)", "Busana etnik atau upcycled fashion", "Toko baju bekas (thrift shop)" ], "Bidang Jasa": [ "Laundry", "Jasa desain grafis", "Makeup Artist (MUA) untuk acara", "Jasa fotografi dan videografi", "Jasa bimbingan belajar (bimbel)", "Jasa digitalisasi usaha" ], "Bidang Kerajinan & Produk Unik": [ "Kerajinan tangan (souvenir, hiasan makrame)", "Lilin aromaterapi", "Produk isi ulang (refill) alami", "Perabotan dari bahan daur ulang" ], "Bidang Lainnya": [ "Toko kelontong atau sembako", "Agribisnis (ikan, sayur, kebun)", "Pertamini (pom bensin mini)", "Usaha otomotif (tambal ban, bengkel)", "Jual pulsa dan layanan pembayaran" ] }; // Gabungkan semua sub-kategori untuk filter flat const ALL_CATEGORIES = Object.values(CATEGORY_GROUPS).flat(); /** * KONFIGURASI FIREBASE (BASE64) * Project ID: katalog-264a4 */ const encodedConfig = "eyJhcGlLZXkiOiJBSXphU3lBNEJHUFkwS1hDXzVNcWtwNm9zLV9oMW0wSlYzSjVzaGMiLCJhdXRoRG9tYWluIjoia2F0YWxvZy0yNjRhNC5maXJlYmFzYXBwLmNvbSIsInByb2plY3RJZCI6ImthdGFsb2ctMjY0YTQiLCJzdG9yYWdlQnVja2V0Ijoia2F0YWxvZy0yNjRhNC5maXJlYmFzdG9yYWdlLmFwcCIsIm1lc3NhZ2luZ1NlbmRlcklkIjoiMjcxNzM3NDA1MDA5IiwiYXBwSWQiOiIxOjE3MTczNzQwNTAwOTp3ZWI6NGU2M2E0MzE5OWYxYmM0YjRmNDI3MCIsIm1lYXN1cmVtZW50SWQiOiJHLTFXUjYwWjhMM0MifQ=="; const getDecodedConfig = () => { try { return JSON.parse(atob(encodedConfig)); } catch (e) { return null; } }; const firebaseConfig = getDecodedConfig(); const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); const appId = "katalog-pekerja-pro"; const App = () => { const [view, setView] = useState('landing'); const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [authError, setAuthError] = useState(null); const [merchants, setMerchants] = useState([]); const [userLocation, setUserLocation] = useState(null); const [isUpdateMode, setIsUpdateMode] = useState(false); const [activeDocId, setActiveDocId] = useState(null); const [filterCat, setFilterCat] = useState(''); const [searchQuery, setSearchQuery] = useState(''); const initialForm = { username: '', password: '', ktp: '', slogan: '', photoUrl: '', businessItems: [{ id: Date.now(), type: '', category: '', locations: [] }] }; const [ownerForm, setOwnerForm] = useState(initialForm); const [loginCreds, setLoginCreds] = useState({ username: '', password: '' }); useEffect(() => { const initAuth = async () => { try { await signInAnonymously(auth); } catch (err) { console.error("Auth error:", err); setAuthError(err.code === 'auth/configuration-not-found' ? "Aktifkan Anonymous Auth di Firebase Console." : err.message); } }; initAuth(); const unsubscribe = onAuthStateChanged(auth, (currUser) => { setUser(currUser); setLoading(false); }); return () => unsubscribe(); }, []); useEffect(() => { if (!user) return; if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (pos) => setUserLocation({ lat: pos.coords.latitude, lng: pos.coords.longitude }), () => console.log("Akses GPS mati") ); } const ref = collection(db, 'artifacts', appId, 'public', 'data', 'merchants'); const unsubscribe = onSnapshot(ref, (snap) => { setMerchants(snap.docs.map(d => ({ id: d.id, ...d.data() }))); }, (err) => console.error("Database Error:", err)); return () => unsubscribe(); }, [user]); const captureLocation = (bIdx) => { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition((pos) => { const updated = [...ownerForm.businessItems]; if (updated[bIdx].locations.length < 3) { updated[bIdx].locations.push({ lat: pos.coords.latitude, lng: pos.coords.longitude }); setOwnerForm({ ...ownerForm, businessItems: updated }); } }, () => alert("Gagal mengambil lokasi. Izinkan GPS.")); } }; const handleSave = async () => { if (!user) return; if (!isUpdateMode) { if (merchants.filter(m => m.ktp === ownerForm.ktp).length >= 3) { return alert("Satu NIK maksimal 3 profil usaha."); } if (merchants.some(m => (m.username || "").toLowerCase() === ownerForm.username.toLowerCase())) { return alert("Username sudah digunakan."); } } const docId = isUpdateMode ? activeDocId : `m_${Date.now()}`; const data = { ...ownerForm, userId: user.uid, updatedAt: new Date().toISOString() }; if (!isUpdateMode) data.serialNumber = merchants.length + 1; try { await setDoc(doc(db, 'artifacts', appId, 'public', 'data', 'merchants', docId), data); alert(isUpdateMode ? "Data Diperbarui!" : "Berhasil Terdaftar!"); setView('landing'); setOwnerForm(initialForm); setIsUpdateMode(false); } catch (e) { alert("Gagal Simpan. Cek Rules Firestore."); } }; const filteredMerchants = merchants.filter(m => { const items = m.businessItems || []; const matchesCat = filterCat === '' || items.some(item => item.category === filterCat); const matchesSearch = searchQuery === '' || items.some(item => (item.type || "").toLowerCase().includes(searchQuery.toLowerCase())) || (m.username || "").toLowerCase().includes(searchQuery.toLowerCase()); return matchesCat && matchesSearch; }); if (authError) return (

Konfigurasi Dibutuhkan

{authError}

); if (loading) return (

Membangun Jaringan Pekerja...

); return (
{/* HEADER */}

{APP_TITLE}

@katalog-264a4
{/* VIEW: LANDING */} {view === 'landing' && (
Pekerja

Solidaritas Ekonomi
Kelas Pekerja

#BeliDariTemanSendiri

)} {/* VIEW: OWNER MENU */} {view === 'owner-menu' && (

Update Data Anda

setLoginCreds({...loginCreds, username: e.target.value})} /> setLoginCreds({...loginCreds, password: e.target.value})} />
)} {/* VIEW: OWNER FORM */} {view === 'owner-form' && (

{isUpdateMode ? 'Update Data' : 'Daftar Anggota'}

setOwnerForm({...ownerForm, username: e.target.value})} /> setOwnerForm({...ownerForm, password: e.target.value})} />
setOwnerForm({...ownerForm, ktp: e.target.value})} /> setOwnerForm({...ownerForm, photoUrl: e.target.value})} /> {ownerForm.businessItems.map((item, bIdx) => (
Jenis Usaha Ke-{bIdx+1}
{ const up = [...ownerForm.businessItems]; up[bIdx].type = e.target.value; setOwnerForm({...ownerForm, businessItems: up}); }} />
{(item.locations || []).map((loc, lIdx) => (
LOKASI {lIdx+1} OK
))} {(item.locations || []).length < 3 && ( )}

*Satu jenis usaha maksimal 3 lokasi.

))} {ownerForm.businessItems.length < 3 && ( )}