feat(settings,admin): every tab and popup in the planner's design

This commit is contained in:
Maurice
2026-10-01 16:00:37 +02:00
parent 4bc7c5cf68
commit 788a58679e
75 changed files with 8134 additions and 8023 deletions
@@ -2,6 +2,9 @@ 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
@@ -23,6 +26,9 @@ function codeTone(code: string): string {
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
@@ -50,54 +56,52 @@ export default function PluginActivityPanel() {
return Number.isNaN(d.getTime()) ? ts : d.toLocaleString(locale)
}
return (
<div className="rounded-xl border border-edge bg-surface-card overflow-hidden">
<div className="px-5 py-4 border-b border-edge-secondary flex items-center gap-3">
<History className="w-4 h-4 flex-shrink-0 text-content-secondary" />
<div className="flex-1 min-w-0">
<h3 className="text-sm font-semibold text-content">{t('settings.pluginActivity.title')}</h3>
<p className="text-xs text-content-muted mt-0.5">{t('settings.pluginActivity.description')}</p>
</div>
<button
type="button"
onClick={load}
disabled={loading}
className="inline-flex items-center gap-1.5 rounded-lg border border-edge px-2.5 py-1.5 text-xs font-medium text-content-secondary hover:text-content disabled:opacity-60"
>
<RefreshCw className={`w-3.5 h-3.5 ${loading ? 'animate-spin' : ''}`} />
{t('settings.pluginActivity.refresh')}
</button>
</div>
const refreshLabel = t('settings.pluginActivity.refresh')
return (
<SettingsCard
icon={History}
title={t('settings.pluginActivity.title')}
hint={t('settings.pluginActivity.description')}
action={
<Tooltip label={refreshLabel}>
<button type="button" onClick={load} disabled={loading} aria-label={refreshLabel} className={SETTINGS_ICON_BUTTON}>
<RefreshCw size={14} className={loading ? 'animate-spin' : undefined} />
</button>
</Tooltip>
}
>
{rows.length === 0 ? (
<p className="px-5 py-6 text-sm text-content-muted">
<SettingsHint>
{loading ? t('common.loading') : t('settings.pluginActivity.empty')}
</p>
</SettingsHint>
) : (
<div className="overflow-x-auto">
<table className="w-full text-sm border-collapse">
<div className={`overflow-x-auto rounded-[12px] border border-edge-faint bg-surface-card ${loading ? 'opacity-60' : ''}`}>
<table className="w-full border-collapse" style={fs(12.5, 'body')}>
<thead>
<tr className="text-left text-xs uppercase tracking-wide text-content-faint border-b border-edge-secondary">
<th className="font-medium px-5 py-2">{t('settings.pluginActivity.columns.plugin')}</th>
<th className="font-medium px-3 py-2">{t('settings.pluginActivity.columns.action')}</th>
<th className="font-medium px-3 py-2">{t('settings.pluginActivity.columns.resource')}</th>
<th className="font-medium px-3 py-2 whitespace-nowrap">{t('settings.pluginActivity.columns.when')}</th>
<th className="font-medium px-5 py-2 text-right">{t('settings.pluginActivity.columns.status')}</th>
<tr className="border-b border-edge-faint bg-surface-secondary" style={fs(10)}>
<th className={`${TH} pl-3.5`}>{t('settings.pluginActivity.columns.plugin')}</th>
<th className={TH}>{t('settings.pluginActivity.columns.action')}</th>
<th className={TH}>{t('settings.pluginActivity.columns.resource')}</th>
<th className={TH}>{t('settings.pluginActivity.columns.when')}</th>
<th className={`${TH} pr-3.5 text-right`}>{t('settings.pluginActivity.columns.status')}</th>
</tr>
</thead>
<tbody>
<tbody className="divide-y divide-edge-faint">
{rows.map((r, i) => (
<tr key={i} className="border-b border-edge-secondary last:border-b-0">
<td className="px-5 py-2.5 text-content align-top">{r.plugin_name || r.plugin_id}</td>
<td className="px-3 py-2.5 align-top">
<span className="font-mono text-xs text-content-secondary">{r.method}</span>
<tr key={i}>
<td className={`${TD} max-w-[200px] pl-3.5`}>
<span className="block truncate font-medium text-content">{r.plugin_name || r.plugin_id}</span>
</td>
<td className="px-3 py-2.5 align-top">
<span className="font-mono text-xs text-content-muted">{r.resource || '—'}</span>
<td className={TD}>
<span className="font-mono text-content-secondary" style={fs(11.5)}>{r.method}</span>
</td>
<td className="px-3 py-2.5 align-top whitespace-nowrap text-content-muted">{fmtWhen(r.ts)}</td>
<td className="px-5 py-2.5 align-top text-right">
<span className={`inline-block rounded-full px-2 py-0.5 text-[11px] font-medium ${codeTone(r.code)}`}>
<td className={`${TD} max-w-[220px]`}>
<span className="block truncate font-mono text-content-muted" style={fs(11.5)} title={r.resource || undefined}>{r.resource || '—'}</span>
</td>
<td className={`${TD} whitespace-nowrap font-geist tabular-nums text-content-muted`}>{fmtWhen(r.ts)}</td>
<td className={`${TD} pr-3.5 text-right`}>
<span className={`inline-block whitespace-nowrap rounded-full px-2 py-[2px] font-geist font-semibold ${codeTone(r.code)}`} style={fs(11)}>
{r.code}
</span>
</td>
@@ -107,6 +111,6 @@ export default function PluginActivityPanel() {
</table>
</div>
)}
</div>
</SettingsCard>
)
}