/* global React, MZ */
const { useState, useEffect } = React;
const ROLE_PL = { admin: 'administrator', moderator: 'moderator', trusted: 'zaufany', member: 'użytkownik' };
function Account({ userId, setRoute, profile: myProfile, onToast }) {
const [u, setU] = useState(null);
const [videos, setVideos] = useState([]);
const [tab, setTab] = useState('videos');
const [loading, setLoading] = useState(true);
const isMe = myProfile?.id === userId;
useEffect(() => {
(async () => {
setLoading(true);
try {
const [prof, vids] = await Promise.all([
MZ.getProfile(userId),
MZ.listUserVideos(userId),
]);
setU(prof);
setVideos(vids);
} catch (e) { onToast('Nie znaleziono użytkownika'); }
setLoading(false);
})();
}, [userId]);
if (loading || !u) return
;
const publishedVideos = videos.filter(v => v.status === 'published');
const totalVotes = publishedVideos.reduce((s, v) => s + (v.up||0) - (v.down||0), 0);
const totalViews = publishedVideos.reduce((s, v) => s + (v.views||0), 0);
return (
{isMe && }
{isMe && }
{tab === 'videos' && (
publishedVideos.length > 0 ? (
{publishedVideos.map(v => setRoute({name:'video', id})} />)}
) :
Brak opublikowanych nagrań.
)}
{tab === 'pending' && isMe && (
{videos.filter(v=>v.status!=='published').length === 0 ?
Brak oczekujących nagrań.
:
videos.filter(v=>v.status!=='published').map(v => (
{v.title}
{v.city || '—'} · {timeAgo(v.created_at)}
{v.status==='pending'?'moderacja':v.status==='rejected'?'odrzucone':'usunięte'}
))
}
)}
{tab === 'settings' && isMe &&
}
);
}
function AccountSettings({ profile, setU, onToast }) {
const [name, setName] = useState(profile.name || '');
const [busy, setBusy] = useState(false);
const save = async (e) => {
e.preventDefault();
setBusy(true);
try {
await MZ.updateProfile({ name });
setU({ ...profile, name });
onToast('Profil zapisany');
} catch (er) { onToast(er.message); }
setBusy(false);
};
const logoutAll = async () => {
if (!confirm('Wylogować ze wszystkich urządzeń?')) return;
await MZ.signOut();
location.reload();
};
return (
);
}
function Admin({ setRoute, onToast }) {
const [section, setSection] = useState('overview');
const [counts, setCounts] = useState({ videos: 0, queue: 0, reports: 0, users: 0 });
useEffect(() => { MZ.stats().then(setCounts); }, [section]);
const sections = [
{ id:'overview', label:'Przegląd' },
{ id:'queue', label:'Kolejka moderacji', count: counts.queue },
{ id:'videos', label:'Wszystkie nagrania', count: counts.videos },
{ id:'reports', label:'Zgłoszenia', count: counts.reports },
{ id:'users', label:'Użytkownicy', count: counts.users },
];
return (
);
}
function AdminOverview({ counts }) {
return (
Opublikowane
{counts.videos}
Otwarte zgłoszenia
{counts.reports}
Użytkownicy
{counts.users}
);
}
function AdminQueue({ onToast }) {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(true);
const load = async () => { setLoading(true); setItems(await MZ.adminListQueue()); setLoading(false); };
useEffect(() => { load(); }, []);
const act = async (id, status) => {
try { await MZ.adminSetStatus(id, status); onToast(status==='published'?'Zatwierdzone':'Odrzucone'); load(); } catch(e){ onToast(e.message); }
};
return (
Moderacja
Kolejka ({items.length})
| Tytuł | Autor | Miasto | Dodano | YT | Akcje |
{loading ? | Ładowanie… |
: items.length === 0 ? | Kolejka pusta. |
:
items.map(q => (
| {q.title} |
@{q.author_profile?.handle || '—'} |
{q.city || '—'} |
{fmtDate(q.created_at)} |
{q.youtube_id} |
|
))
}
);
}
function AdminVideos({ setRoute, onToast }) {
const [items, setItems] = useState([]);
const [sev, setSev] = useState('all');
const load = async () => setItems(await MZ.listVideos({ status:'published', limit:500 }));
useEffect(() => { load(); }, []);
const list = sev === 'all' ? items : items.filter(v => v.severity === sev);
const remove = async (id) => {
if (!confirm('Usunąć nagranie?')) return;
try { await MZ.adminSetStatus(id, 'removed'); onToast('Usunięte'); load(); } catch(e){ onToast(e.message); }
};
return (
Treści
Wszystkie nagrania ({items.length})
{['all','collision','near-miss','wildlife','weather','other'].map(s => (
))}
);
}
function AdminReports({ onToast }) {
const [items, setItems] = useState([]);
const load = async () => setItems(await MZ.adminListReports());
useEffect(() => { load(); }, []);
const act = async (id, patch) => {
try { await MZ.adminUpdateReport(id, patch); onToast('Zaktualizowane'); load(); } catch(e) { onToast(e.message); }
};
return (
Bezpieczeństwo
Zgłoszenia ({items.length})
| Nagranie | Zgłaszający | Powód | Otwarte | Status | Akcje |
{items.length === 0 ? | Brak zgłoszeń. |
:
items.map(r => (
| {r.video?.title || '—'} |
@{r.reporter_profile?.handle || '—'} |
{r.reason}{r.details ? ` — ${r.details}` : ''} |
{fmtDate(r.created_at)} |
{r.status} |
|
))
}
);
}
function AdminUsers({ setRoute, onToast }) {
const [items, setItems] = useState([]);
const load = async () => setItems(await MZ.adminListUsers());
useEffect(() => { load(); }, []);
const setRole = async (id, role) => {
try { await MZ.adminUpdateUserRole(id, role); onToast('Rola zmieniona'); load(); } catch(e) { onToast(e.message); }
};
return (
Osoby
Użytkownicy ({items.length})
);
}
Object.assign(window, { Account, Admin });