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:
@@ -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`;
|
||||
|
||||
Reference in New Issue
Block a user