import { useEffect, useState } from 'react' import { History, RefreshCw } from 'lucide-react' import { pluginsApi } from '../../api/client' import { useTranslation } from '../../i18n' import { Tooltip } from '../shared/Tooltip' import { fs } from '../shared/DialogShell' import { SettingsCard, SettingsHint, SETTINGS_ICON_BUTTON } from './settingsKit' interface ActivityRow { ts: string plugin_id: string plugin_name: string | null method: string resource: string | null code: string } /** * Tailwind classes for a result-code pill. "ok" stays neutral; an access denial * reads as danger, anything else non-ok as a softer warning. Kept subtle — this * is meant to be a quiet log, not an alert wall. */ function codeTone(code: string): string { if (code === 'ok') return 'bg-surface-hover text-content-secondary' if (/FORBIDDEN|DENIED|UNAUTHORIZED/i.test(code)) return 'bg-danger-soft text-danger' return 'bg-warning-soft text-warning' } const TH = 'whitespace-nowrap px-3 py-2.5 text-left font-geist font-bold uppercase tracking-[.08em] text-content-faint' const TD = 'px-3 py-3 align-middle' /** * The signed-in user's own plugin activity log — every host-mediated action a * plugin took while bound to them, newest first. The user-facing half of the * capability audit; it's what keeps the deliberately broad read grants * accountable to the person whose data was read. Fail-safe: a failed load just * shows the empty state, never a crash. */ export default function PluginActivityPanel() { const { t, locale } = useTranslation() const [rows, setRows] = useState([]) const [loading, setLoading] = useState(true) const load = () => { setLoading(true) pluginsApi.myActivity() .then(r => setRows(r.activity)) .catch(() => setRows([])) .finally(() => setLoading(false)) } useEffect(() => { load() }, []) const fmtWhen = (ts: string): string => { const d = new Date(ts) return Number.isNaN(d.getTime()) ? ts : d.toLocaleString(locale) } const refreshLabel = t('settings.pluginActivity.refresh') return ( } > {rows.length === 0 ? ( {loading ? t('common.loading') : t('settings.pluginActivity.empty')} ) : (
{rows.map((r, i) => ( ))}
{t('settings.pluginActivity.columns.plugin')} {t('settings.pluginActivity.columns.action')} {t('settings.pluginActivity.columns.resource')} {t('settings.pluginActivity.columns.when')} {t('settings.pluginActivity.columns.status')}
{r.plugin_name || r.plugin_id} {r.method} {r.resource || '—'} {fmtWhen(r.ts)} {r.code}
)}
) }