// directory.jsx — business directory + shared bottom sheets. Exported to window. // ---------- generic bottom sheet ---------- function Sheet({ children, onClose, height = '72%' }) { return (
e.stopPropagation()} style={{ height, background: 'var(--surface)', borderTopLeftRadius: 24, borderTopRightRadius: 24, boxShadow: 'var(--sh-up)', display: 'flex', flexDirection: 'column', overflow: 'hidden', animation: 'gsSheetUp 280ms var(--ease)' }}>
{children}
); } function SheetAction({ icon, label, onClick, primary }) { return ( ); } // ---------- place detail sheet ---------- function PlaceSheet({ place, onClose }) { const dc = DIR_CATS.find(d => d.key === place.cat) || DIR_CATS[0]; return (
{place.openNow ? BUKA : TUTUP}
{dc.label} {place.sponsored && FEATURED}

{place.name}

{place.rating} ({place.reviews}) {place.area}

{place.tagline}

{[['clock', 'Jam buka', place.hours], ['phone', 'Telepon', place.phone], ['pin', 'Alamat', place.area + ', Gading Serpong']].map(([ic, k, v]) => (
{k} {v}
))}
); } // ---------- report detail sheet ---------- function CommentsSection({ reportId, authed, onRequireAuth }) { const [list, setList] = React.useState(null); const [body, setBody] = React.useState(''); const [busy, setBusy] = React.useState(false); const load = React.useCallback(() => { gsData.loadComments(reportId).then(setList); }, [reportId]); React.useEffect(() => { load(); }, [load]); const send = async () => { if (!authed) { onRequireAuth && onRequireAuth(); return; } if (!body.trim() || busy) return; setBusy(true); const res = await gsData.addComment(reportId, body.trim()); setBusy(false); if (!res || !res.error) { setBody(''); load(); } }; return (

Diskusi {list ? '(' + list.length + ')' : ''}

{list === null &&
Memuat...
} {list && list.length === 0 &&
Belum ada komentar. Mulai diskusi.
} {list && list.map(cm => (
{(cm.author || 'W')[0].toUpperCase()}
{cm.author} {cm.time}
{cm.body}
))}
setBody(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') send(); }} placeholder={authed ? "Tulis komentar..." : "Masuk untuk berkomentar"} onFocus={() => { if (!authed && onRequireAuth) onRequireAuth(); }} style={{ flex: 1, padding: '11px 14px', borderRadius: 'var(--r-chip)', border: '1px solid var(--border)', background: 'var(--surface-2)', fontFamily: 'var(--font)', fontSize: 13.5, color: 'var(--text-1)', outline: 'none' }} />
); } function ReportDetailSheet({ report, onClose, myUps = {}, onUp, authed, onRequireAuth }) { const c = CATS[report.cat] || CATS.lainnya; const upActive = !!myUps[report.id]; const tl = { open: [['Dilaporkan', report.time, true], ['Ditinjau pengurus', 'menunggu', false]], proses: [['Dilaporkan', report.time, true], ['Diterima pengurus', 'aktif', true], ['Sedang ditangani', 'aktif', true], ['Selesai', 'menunggu', false]], selesai: [['Dilaporkan', report.time, true], ['Ditangani', 'selesai', true], ['Selesai', 'beres', true]], }[report.status]; return (
{report.photo && report.photo_url && (
)}
{c.label}

{report.title}

{report.area} · {report.time}
{report.desc &&

{report.desc}

} {report.instagram_url && ( Lihat di Instagram )}

Status

{tl.map(([label, sub, done], i) => (
{done && } {i < tl.length - 1 && }
{label}
{sub}
))}
{report.anon ? '?' : (report.reporter || 'W').slice(0, 1)}
Dilaporkan oleh
{report.reporter}
onUp && onUp(report.id)}>
{report.comments} komentar
); } // ---------- sponsor banner ---------- function SponsorBanner({ places = [] }) { const sponsors = places.filter(p => p.sponsored); const [i, setI] = React.useState(0); React.useEffect(() => { const id = setInterval(() => setI(v => (v + 1) % sponsors.length), 4200); return () => clearInterval(id); }, [sponsors.length]); if (!sponsors.length) return null; const s = sponsors[i]; return (
SPONSORED
{s.name}
{s.tagline}
Lihat Tempat
{sponsors.map((_, j) => )}
); } // ---------- directory screen ---------- function Directory({ dark, openPlace, t, places = [] }) { const [cat, setCat] = React.useState('all'); const [view, setView] = React.useState('grid'); const shown = (t.showSponsored ? places : places.filter(p => !p.sponsored)) .filter(p => cat === 'all' || p.cat === cat); return (

Direktori

setCat('all')}>Semua {DIR_CATS.map(d => ( setCat(d.key)} color={d.color}> {d.label} ))}
{places.some(p => p.sponsored) && (
)}
{shown.length} tempat
{['grid', 'list'].map(v => ( ))}
{view === 'grid' ? (
{shown.length === 0 ? (
Direktori bisnis segera hadir.
) : shown.map(p => )}
) : (
{shown.map(p => )}
)}
); } function PlaceRow({ place, onOpen }) { const dc = DIR_CATS.find(d => d.key === place.cat) || DIR_CATS[0]; return ( ); } Object.assign(window, { Directory, PlaceSheet, ReportDetailSheet, SponsorBanner, PlaceRow, Sheet, CommentsSection });