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' && (
Solidaritas Ekonomi
Kelas Pekerja
#BeliDariTemanSendiri
)}
{/* VIEW: OWNER MENU */}
{view === 'owner-menu' && (
)}
{/* 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 && (
)}
)}
{/* VIEW: BUYER SEARCH */}
{view === 'buyer-view' && (
{userLocation &&
}
{filteredMerchants.map((m, idx) => (m.businessItems || []).map((item, iIdx) => (item.locations || []).map((loc, lIdx) => (
{String(item.type || "")}
))))}
Anggota Terdaftar ({filteredMerchants.length})
{filteredMerchants.length === 0 ?
Belum Ada Penjual di Kategori Ini...
:
filteredMerchants.map(m => (

e.target.src="https://via.placeholder.com/300?text=Kelas+Pekerja"} />
{m.username}
ID #{m.serialNumber}
{(m.businessItems || []).map((b, bi) => (
{b.type || b.category}
))}
"{m.slogan}"
))
}
)}
);
};
export default App;