fix: align frontend interfaces with backend DTO field names

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-17 16:36:11 +01:00
parent 329e4b0b16
commit 038b663b8c
7 changed files with 153 additions and 168 deletions

View File

@@ -10,7 +10,7 @@ import {
useDatabaseQueries,
useKillQuery,
} from '../../api/queries/admin/database';
import { useThresholds, useSaveThresholds, type Thresholds } from '../../api/queries/admin/thresholds';
import { useThresholds, useSaveThresholds, type ThresholdConfig } from '../../api/queries/admin/thresholds';
import styles from './DatabaseAdminPage.module.css';
export function DatabaseAdminPage() {
@@ -78,13 +78,13 @@ function DatabaseAdminContent() {
<PoolSection
pool={pool}
warningPct={thresholds.data?.poolWarningPercent}
criticalPct={thresholds.data?.poolCriticalPercent}
warningPct={thresholds.data?.database?.connectionPoolWarning}
criticalPct={thresholds.data?.database?.connectionPoolCritical}
/>
<TablesSection tables={tables} />
<QueriesSection
queries={queries}
warningSeconds={thresholds.data?.queryDurationWarningSeconds}
warningSeconds={thresholds.data?.database?.queryDurationWarning}
/>
<MaintenanceSection />
<ThresholdsSection thresholds={thresholds.data} />
@@ -104,8 +104,8 @@ function PoolSection({
const data = pool.data;
if (!data) return null;
const usagePct = data.maxConnections > 0
? Math.round((data.activeConnections / data.maxConnections) * 100)
const usagePct = data.maxPoolSize > 0
? Math.round((data.activeConnections / data.maxPoolSize) * 100)
: 0;
const barColor =
criticalPct && usagePct >= criticalPct ? '#ef4444'
@@ -121,7 +121,7 @@ function PoolSection({
>
<div className={styles.progressContainer}>
<div className={styles.progressLabel}>
{data.activeConnections} / {data.maxConnections} connections
{data.activeConnections} / {data.maxPoolSize} connections
<span className={styles.progressPct}>{usagePct}%</span>
</div>
<div className={styles.progressBar}>
@@ -141,11 +141,11 @@ function PoolSection({
<span className={styles.metricLabel}>Idle</span>
</div>
<div className={styles.metric}>
<span className={styles.metricValue}>{data.pendingConnections}</span>
<span className={styles.metricValue}>{data.pendingThreads}</span>
<span className={styles.metricLabel}>Pending</span>
</div>
<div className={styles.metric}>
<span className={styles.metricValue}>{data.maxWaitMillis}ms</span>
<span className={styles.metricValue}>{data.maxWaitMs}ms</span>
<span className={styles.metricLabel}>Max Wait</span>
</div>
</div>
@@ -179,7 +179,7 @@ function TablesSection({ tables }: { tables: ReturnType<typeof useDatabaseTables
{data.map((t) => (
<tr key={t.tableName}>
<td className={styles.mono}>{t.tableName}</td>
<td>{t.rowEstimate.toLocaleString()}</td>
<td>{t.rowCount.toLocaleString()}</td>
<td>{t.dataSize}</td>
<td>{t.indexSize}</td>
</tr>
@@ -203,7 +203,7 @@ function QueriesSection({
const killMutation = useKillQuery();
const data = queries.data;
const warningMs = (warningSeconds ?? 30) * 1000;
const warningSec = warningSeconds ?? 30;
return (
<RefreshableCard
@@ -230,10 +230,10 @@ function QueriesSection({
{data.map((q) => (
<tr
key={q.pid}
className={q.durationMs > warningMs ? styles.rowWarning : undefined}
className={q.durationSeconds > warningSec ? styles.rowWarning : undefined}
>
<td className={styles.mono}>{q.pid}</td>
<td>{formatDuration(q.durationMs)}</td>
<td>{formatDuration(q.durationSeconds)}</td>
<td>{q.state}</td>
<td className={styles.queryCell} title={q.query}>
{q.query.length > 100 ? `${q.query.slice(0, 100)}...` : q.query}
@@ -287,16 +287,19 @@ function MaintenanceSection() {
);
}
function ThresholdsSection({ thresholds }: { thresholds?: Thresholds }) {
const [form, setForm] = useState<Thresholds | null>(null);
function ThresholdsSection({ thresholds }: { thresholds?: ThresholdConfig }) {
const [form, setForm] = useState<ThresholdConfig | null>(null);
const saveMutation = useSaveThresholds();
const [status, setStatus] = useState<{ type: 'success' | 'error'; msg: string } | null>(null);
const current = form ?? thresholds;
if (!current) return null;
function update(key: keyof Thresholds, value: number) {
setForm((prev) => ({ ...(prev ?? thresholds!), [key]: value }));
function updateDb(key: keyof ThresholdConfig['database'], value: number) {
setForm((prev) => {
const base = prev ?? thresholds!;
return { ...base, database: { ...base.database, [key]: value } };
});
}
async function handleSave() {
@@ -319,8 +322,8 @@ function ThresholdsSection({ thresholds }: { thresholds?: Thresholds }) {
<input
type="number"
className={styles.thresholdInput}
value={current.poolWarningPercent}
onChange={(e) => update('poolWarningPercent', Number(e.target.value))}
value={current.database.connectionPoolWarning}
onChange={(e) => updateDb('connectionPoolWarning', Number(e.target.value))}
/>
</div>
<div className={styles.thresholdField}>
@@ -328,8 +331,8 @@ function ThresholdsSection({ thresholds }: { thresholds?: Thresholds }) {
<input
type="number"
className={styles.thresholdInput}
value={current.poolCriticalPercent}
onChange={(e) => update('poolCriticalPercent', Number(e.target.value))}
value={current.database.connectionPoolCritical}
onChange={(e) => updateDb('connectionPoolCritical', Number(e.target.value))}
/>
</div>
<div className={styles.thresholdField}>
@@ -337,8 +340,8 @@ function ThresholdsSection({ thresholds }: { thresholds?: Thresholds }) {
<input
type="number"
className={styles.thresholdInput}
value={current.queryDurationWarningSeconds}
onChange={(e) => update('queryDurationWarningSeconds', Number(e.target.value))}
value={current.database.queryDurationWarning}
onChange={(e) => updateDb('queryDurationWarning', Number(e.target.value))}
/>
</div>
<div className={styles.thresholdField}>
@@ -346,8 +349,8 @@ function ThresholdsSection({ thresholds }: { thresholds?: Thresholds }) {
<input
type="number"
className={styles.thresholdInput}
value={current.queryDurationCriticalSeconds}
onChange={(e) => update('queryDurationCriticalSeconds', Number(e.target.value))}
value={current.database.queryDurationCritical}
onChange={(e) => updateDb('queryDurationCritical', Number(e.target.value))}
/>
</div>
</div>
@@ -370,9 +373,9 @@ function ThresholdsSection({ thresholds }: { thresholds?: Thresholds }) {
);
}
function formatDuration(ms: number): string {
if (ms < 1000) return `${ms}ms`;
const s = Math.floor(ms / 1000);
function formatDuration(seconds: number): string {
if (seconds < 1) return `${Math.round(seconds * 1000)}ms`;
const s = Math.floor(seconds);
if (s < 60) return `${s}s`;
const m = Math.floor(s / 60);
return `${m}m ${s % 60}s`;