// Florio Analytics — Feedback tab (ported from js/pages/feedback.js). // Lists in-app feedback from the `feedback` collection, split into Unread / Read. // Each card can be toggled read/unread (writes `read` back to Firestore). // Rebuilt in the new design kit. All queries / interactions preserved. (function () { const NS = window.HyperChartsDesignSystem_e51a95; const I = window.HyperIcons; const { useState, useEffect, useCallback } = React; function fmt(n) { return (window.FlorioAPI && window.FlorioAPI.formatNumber) ? window.FlorioAPI.formatNumber(n) : String(n ?? 0); } function formatDate(item) { if (item.createdAt && typeof item.createdAt.toDate === 'function') { return item.createdAt.toDate().toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit', }); } return 'Unknown date'; } function FeedbackCard({ item, isRead, onToggle, busy }) { return (
{item.email || 'No email'} {formatDate(item)}

{item.message}

); } function Feedback({ range }) { const P = window.PA; const [items, setItems] = useState(null); const [busyId, setBusyId] = useState(null); const load = useCallback(async () => { const { db, collection, query, orderBy, getDocs } = window.FlorioDB; // Data source: `feedback` collection, ordered by createdAt desc. const q = query(collection(db, 'feedback'), orderBy('createdAt', 'desc')); const snap = await getDocs(q); const out = []; snap.forEach((d) => out.push(Object.assign({ id: d.id }, d.data()))); return out; }, []); useEffect(() => { let alive = true; (async () => { await window.FlorioReady; try { const out = await load(); if (alive) setItems(out); } catch (e) { console.error('[Feedback] load failed:', e); if (alive) setItems([]); } })(); return () => { alive = false; }; }, [load]); // Toggle read state → write to Firestore, then update local list. const toggleRead = useCallback(async (id, markAsRead) => { const { db, doc, updateDoc } = window.FlorioDB; setBusyId(id); try { await updateDoc(doc(db, 'feedback', id), { read: markAsRead }); setItems((cur) => (cur || []).map((it) => (it.id === id ? Object.assign({}, it, { read: markAsRead }) : it))); } catch (e) { console.error('[Feedback] toggle failed:', e); alert('Update failed: ' + e.message); } finally { setBusyId(null); } }, []); // Loading spinner while data null. if (!items) { return
; } const unread = items.filter((it) => !it.read); const read = items.filter((it) => it.read); const Empty = ({ note }) => (
{I.heart({ size: 24 })} {note}
); return (
{/* KPI row */}
{unread.length === 0 ? : (
{unread.map((it) => )}
)}
{read.length === 0 ? : (
{read.map((it) => )}
)}
); } (window.FlorioTabs = window.FlorioTabs || {}).feedback = Feedback; })();