'use client'; import React, { FC, useCallback, useState } from 'react'; import useSWR from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { Button } from '@gitroom/react/form/button'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; interface PerSocial { provider: string; count: number; } interface StatsBlock { total: number; perSocial: PerSocial[]; } interface StatsResponse { from: string; to: string; errors: StatsBlock; posts: StatsBlock; connected: StatsBlock; } const isoDaysAgo = (days: number) => { const d = new Date(); d.setDate(d.getDate() - days); return d.toISOString().slice(0, 10); }; const today = () => new Date().toISOString().slice(0, 10); const startOfWeek = () => { const d = new Date(); // ISO week: Monday = 0 const day = (d.getDay() + 6) % 7; d.setDate(d.getDate() - day); return d.toISOString().slice(0, 10); }; const startOfMonth = () => { const d = new Date(); d.setDate(1); return d.toISOString().slice(0, 10); }; const PRESETS: { label: string; range: () => { from: string; to: string } }[] = [ { label: 'Today', range: () => ({ from: today(), to: today() }) }, { label: 'This week', range: () => ({ from: startOfWeek(), to: today() }) }, { label: 'This month', range: () => ({ from: startOfMonth(), to: today() }) }, { label: 'Last 7 days', range: () => ({ from: isoDaysAgo(7), to: today() }) }, { label: 'Last 30 days', range: () => ({ from: isoDaysAgo(30), to: today() }) }, ]; const useStats = (params: { from: string; to: string; unknownOnly: boolean; }) => { const fetch = useFetch(); const query = new URLSearchParams({ from: params.from, to: params.to, ...(params.unknownOnly ? { unknownOnly: 'true' } : {}), }); const key = `/admin/stats?${query.toString()}`; return useSWR( key, async (url: string) => { const res = await fetch(url); if (!res.ok) { throw new Error('Failed to load stats'); } return res.json(); }, { revalidateOnFocus: false, revalidateOnReconnect: false, } ); }; const SummaryCard: FC<{ label: string; value: number }> = ({ label, value, }) => (
{label}
{value.toLocaleString()}
); const PerSocialTable: FC<{ title: string; block: StatsBlock }> = ({ title, block, }) => (
{title}
Count
{block.perSocial.length === 0 ? (
No data for this timeframe.
) : ( block.perSocial.map((row) => (
{row.provider}
{row.count.toLocaleString()}
)) )}
); export const AdminStatsComponent: FC = () => { const user = useUser(); const [fromInput, setFromInput] = useState(today()); const [toInput, setToInput] = useState(today()); const [range, setRange] = useState({ from: today(), to: today() }); const [unknownOnly, setUnknownOnly] = useState(false); const { data, isLoading, error } = useStats({ ...range, unknownOnly }); const applyRange = useCallback((next: { from: string; to: string }) => { setFromInput(next.from); setToInput(next.to); setRange(next); }, []); if (!user?.isSuperAdmin) { return (
You do not have access to this page.
); } return (
Admin Stats
{data && (
{new Date(data.from).toLocaleDateString()} —{' '} {new Date(data.to).toLocaleDateString()}
)}
{PRESETS.map((preset) => { const next = preset.range(); const active = range.from === next.from && range.to === next.to; return ( ); })}
From
setFromInput(e.target.value)} className="bg-newBgColorInner h-[38px] border border-newTableBorder rounded-[8px] px-[10px] text-[14px] text-textColor" />
To
setToInput(e.target.value)} className="bg-newBgColorInner h-[38px] border border-newTableBorder rounded-[8px] px-[10px] text-[14px] text-textColor" />
{isLoading ? ( ) : error || !data ? (
Failed to load stats.
) : ( <>
)}
); };