From 635f9851bcb58add60596e25f74030dc725e0fbd Mon Sep 17 00:00:00 2001 From: Eyejoker Date: Mon, 27 Apr 2026 01:10:40 +0900 Subject: [PATCH] fix(dashboard): make quota windows first-class (#28) --- apps/dashboard/src/App.tsx | 71 +++++++++++++------------- apps/dashboard/src/styles.css | 94 ++++++++++++++++------------------- 2 files changed, 76 insertions(+), 89 deletions(-) diff --git a/apps/dashboard/src/App.tsx b/apps/dashboard/src/App.tsx index 2151ac8..e27f670 100644 --- a/apps/dashboard/src/App.tsx +++ b/apps/dashboard/src/App.tsx @@ -140,12 +140,6 @@ function usagePeak(row: UsageRow): number { return Math.max(row.h5pct, row.d7pct); } -function usageRemaining(row: UsageRow): number | null { - const peak = usagePeak(row); - if (peak < 0) return null; - return Math.max(0, 100 - peak); -} - function usageLimitWindow(row: UsageRow): UsageLimitWindow { return row.d7pct >= row.h5pct ? 'd7' : 'h5'; } @@ -187,8 +181,8 @@ function usageNameParts(row: UsageRow): { return { account: cleaned, plan: null }; } -function usageLimitReset(row: UsageRow): string { - return (usageLimitWindow(row) === 'd7' ? row.d7reset : row.h5reset).trim(); +function usageWindowReset(row: UsageRow, window: UsageLimitWindow): string { + return (window === 'd7' ? row.d7reset : row.h5reset).trim(); } function usageBurnRate(row: UsageRow): number | null { @@ -929,50 +923,36 @@ function RoomPanel({ ); } -function UsageRemainingMeter({ +function UsageQuotaMeter({ row, rowName, + window, t, }: { row: UsageRow; rowName: string; + window: UsageLimitWindow; t: Messages; }) { - const remaining = usageRemaining(row); - const window = usageLimitWindow(row); - const h5Remaining = usageWindowRemaining(row, 'h5'); - const d7Remaining = usageWindowRemaining(row, 'd7'); - const reset = usageLimitReset(row); + const remaining = usageWindowRemaining(row, window); + const reset = usageWindowReset(row, window); + const tightest = usageLimitWindow(row) === window; + const label = t.usage.quota[window]; return ( -
+
- {t.usage.remaining} + {label} {remaining === null ? '-' : formatPct(remaining)}
-
- - {t.usage.quota.h5} - {h5Remaining === null ? '-' : formatPct(h5Remaining)} - - - - {t.usage.quota.d7} - {d7Remaining === null ? '-' : formatPct(d7Remaining)} - - -
- - {remaining === null ? '-' : t.usage.limitBasis[window]} - {reset ? ` · ${t.usage.reset} ${reset}` : ` · ${t.usage.noReset}`} - + {reset ? `${t.usage.reset} ${reset}` : t.usage.noReset}
); } @@ -1017,8 +997,13 @@ function UsagePanel({ const focusValue = focusRows .map((row) => { const { account } = usageNameParts(row); - const remaining = usageRemaining(row); - return `${account} ${remaining === null ? '-' : formatPct(remaining)}`; + const h5Remaining = usageWindowRemaining(row, 'h5'); + const d7Remaining = usageWindowRemaining(row, 'd7'); + return `${account} ${t.usage.quota.h5} ${ + h5Remaining === null ? '-' : formatPct(h5Remaining) + } · ${t.usage.quota.d7} ${ + d7Remaining === null ? '-' : formatPct(d7Remaining) + }`; }) .join(' · '); const groups = [ @@ -1050,7 +1035,8 @@ function UsagePanel({
{t.usage.usage} - {t.usage.remaining} + {t.usage.quota.h5} + {t.usage.quota.d7} {t.usage.speed}
{groups.map((group) => ( @@ -1077,7 +1063,18 @@ function UsagePanel({ ) : null}
- + + ); diff --git a/apps/dashboard/src/styles.css b/apps/dashboard/src/styles.css index 0d408f4..e615733 100644 --- a/apps/dashboard/src/styles.css +++ b/apps/dashboard/src/styles.css @@ -622,7 +622,7 @@ td small { } .usage-summary span, -.usage-remaining small { +.usage-quota small { color: var(--muted); } @@ -642,10 +642,9 @@ td small { .usage-matrix-head, .usage-row { display: grid; - grid-template-columns: minmax(132px, 0.78fr) minmax(0, 1fr) minmax( - 96px, - 0.46fr - ); + grid-template-columns: + minmax(132px, 0.82fr) minmax(82px, 0.42fr) minmax(82px, 0.42fr) + minmax(84px, 0.34fr); gap: 8px; align-items: center; } @@ -693,7 +692,7 @@ td small { } .usage-account, -.usage-remaining div { +.usage-quota div { display: flex; min-width: 0; gap: 8px; @@ -723,7 +722,7 @@ td small { white-space: nowrap; } -.usage-remaining span, +.usage-quota span, .usage-speed span { color: var(--muted); font-size: 11px; @@ -732,57 +731,42 @@ td small { text-transform: uppercase; } -.usage-remaining, +.usage-quota, .usage-speed { display: grid; min-width: 0; gap: 5px; } -.quota-pair { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 5px; +.usage-quota { + padding: 6px; + border-radius: 13px; + background: rgba(43, 55, 38, 0.035); } -.quota-pair span { - display: grid; - min-width: 0; - gap: 3px; - padding: 5px; - border-radius: 11px; - background: rgba(43, 55, 38, 0.045); -} - -.quota-pair span.is-tight { - background: rgba(191, 95, 44, 0.11); +.usage-quota-tight { + background: rgba(191, 95, 44, 0.1); box-shadow: inset 0 0 0 1px rgba(191, 95, 44, 0.16); } -.quota-pair b, -.quota-pair em { +.usage-quota strong { + font-size: 17px; + line-height: 1; +} + +.usage-quota small { min-width: 0; overflow: hidden; + color: var(--muted); font-size: 10px; - font-style: normal; - font-weight: 900; - line-height: 1; text-overflow: ellipsis; white-space: nowrap; } -.quota-pair b { - color: var(--muted); -} - -.quota-pair progress { - height: 5px; -} - .usage-speed { - min-height: 54px; + min-height: 48px; align-content: center; - padding: 8px; + padding: 6px; border-radius: 14px; background: rgba(43, 55, 38, 0.045); } @@ -1603,8 +1587,10 @@ progress::-moz-progress-bar { } .usage-row { - grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr) minmax(90px, 0.5fr); - gap: 6px; + grid-template-columns: + minmax(86px, 0.86fr) minmax(62px, 0.54fr) minmax(62px, 0.54fr) + minmax(58px, 0.4fr); + gap: 5px; padding: 9px; } @@ -1613,30 +1599,26 @@ progress::-moz-progress-bar { gap: 5px; } - .usage-remaining, + .usage-quota, .usage-speed { gap: 4px; } - .usage-remaining div { + .usage-quota { + padding: 5px; + } + + .usage-quota div { gap: 6px; } - .usage-remaining small { + .usage-quota small { overflow: hidden; - font-size: 11px; + font-size: 10px; text-overflow: ellipsis; white-space: nowrap; } - .quota-pair { - gap: 4px; - } - - .quota-pair span { - padding: 4px; - } - .record-card-grid { grid-template-columns: 1fr 1fr; } @@ -1732,6 +1714,14 @@ progress::-moz-progress-bar { grid-column: 1 / -1; } + .usage-speed { + grid-column: 1 / -1; + display: flex; + min-height: auto; + align-items: center; + justify-content: space-between; + } + .skeleton-grid, .ops-strip { gap: 8px;