-
Notifications
You must be signed in to change notification settings - Fork 103
Add Auth Files quota estimate details modal #230
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -845,6 +845,12 @@ function isAuthFileDisplayMode(value: string | null | undefined): value is AuthF | |
|
|
||
| export function AuthFileQuotaPanel({ row, quotaUsageMode }: { row: AuthFileCredentialRow; quotaUsageMode: QuotaUsageMode }) { | ||
| const { t } = useTranslation() | ||
| const [selectedQuota, setSelectedQuota] = useState<DisplayQuota | null>(null) | ||
| const [quotaDetailsOpen, setQuotaDetailsOpen] = useState(false) | ||
| const openQuotaDetails = (quota: DisplayQuota) => { | ||
| setSelectedQuota(quota) | ||
| setQuotaDetailsOpen(true) | ||
| } | ||
|
|
||
| // 限额区域按加载、错误、刷新中、无缓存、可展示数据的顺序降级。 | ||
| if (row.quotaLoading) { | ||
|
|
@@ -872,8 +878,9 @@ export function AuthFileQuotaPanel({ row, quotaUsageMode }: { row: AuthFileCrede | |
| <div className={styles.credentialQuotaPanel}> | ||
| <div className={styles.credentialQuotaBars}> | ||
| {/* 每个可计算进度的 quota 都独占一个稳定块;不可进度化 quota 在 view model 中已过滤。 */} | ||
| {row.displayQuotas.map((quota) => <QuotaBar key={quota.key} quota={quota} quotaUsageMode={quotaUsageMode} />)} | ||
| {row.displayQuotas.map((quota) => <QuotaBar key={quota.key} quota={quota} quotaUsageMode={quotaUsageMode} onSelect={openQuotaDetails} />)} | ||
| </div> | ||
| <QuotaDetailsModal quota={selectedQuota} open={quotaDetailsOpen} onClose={() => setQuotaDetailsOpen(false)} /> | ||
| </div> | ||
| ) | ||
| } | ||
|
|
@@ -1088,7 +1095,7 @@ export function formatQuotaBillingUsageAriaLabel(t: Translate, billingUsage: Non | |
| }) | ||
| } | ||
|
|
||
| function QuotaBar({ quota, quotaUsageMode }: { quota: DisplayQuota; quotaUsageMode: QuotaUsageMode }) { | ||
| function QuotaBar({ quota, quotaUsageMode, onSelect }: { quota: DisplayQuota; quotaUsageMode: QuotaUsageMode; onSelect: (quota: DisplayQuota) => void }) { | ||
| const { t } = useTranslation() | ||
| // 条宽使用剩余额度百分比,颜色跟随剩余风险状态从绿到黄到红。 | ||
| const percent = quota.barPercent ?? 0 | ||
|
|
@@ -1100,7 +1107,7 @@ function QuotaBar({ quota, quotaUsageMode }: { quota: DisplayQuota; quotaUsageMo | |
| const windowUsage = billingUsage ? undefined : quotaWindowUsageForMode(quota, quotaUsageMode) | ||
|
|
||
| return ( | ||
| <div className={styles.credentialQuotaBarBlock}> | ||
| <button type="button" className={styles.credentialQuotaBarBlock} onClick={() => onSelect(quota)} aria-label={t('usage_stats.credentials_quota_details_open', { label: quota.label })}> | ||
| <div className={styles.credentialQuotaBarHeader}> | ||
| <span className={styles.credentialQuotaLabelGroup}> | ||
| <span>{quota.label}</span> | ||
|
|
@@ -1138,7 +1145,7 @@ function QuotaBar({ quota, quotaUsageMode }: { quota: DisplayQuota; quotaUsageMo | |
| )} | ||
| {resetLabel && <span>{resetLabel}</span>} | ||
| </div> | ||
| </div> | ||
| </button> | ||
| ) | ||
| } | ||
|
|
||
|
|
@@ -1155,3 +1162,55 @@ function quotaWindowUsageForMode(quota: DisplayQuota, mode: QuotaUsageMode): Dis | |
| } | ||
| return quota.windowUsage | ||
| } | ||
|
|
||
| function QuotaDetailsModal({ quota, open, onClose }: { quota: DisplayQuota | null; open: boolean; onClose: () => void }) { | ||
| const { t } = useTranslation() | ||
| const currentUsage = quota?.windowUsage | ||
| const estimatedUsage = quota?.windowUsageEstimate | ||
| const billingUsage = quota?.billingUsage | ||
| const percentLabel = quota?.barPercent === null || quota?.barPercent === undefined ? '' : `${Math.round(quota.barPercent)}%` | ||
| const resetLabel = quota?.resetText ? formatQuotaResetLabel(quota.resetText) : '' | ||
|
|
||
| return ( | ||
| <Modal open={open} title={t('usage_stats.credentials_quota_details_title', { label: quota?.label ?? '' })} onClose={onClose} width={520} className={styles.credentialQuotaDetailsModal}> | ||
|
Comment on lines
+1166
to
+1175
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since We can optimize this by returning Additionally, because |
||
| <div className={styles.credentialQuotaDetailsPanel}> | ||
| {billingUsage ? ( | ||
| <div className={styles.credentialQuotaDetailsGrid}> | ||
| <QuotaDetailMetric label={t('usage_stats.credentials_quota_details_billing_used')} value={billingUsage.used ?? t('usage_stats.credentials_quota_details_no_value')} /> | ||
| <QuotaDetailMetric label={t('usage_stats.credentials_quota_details_billing_limit')} value={billingUsage.limit ?? t('usage_stats.credentials_quota_details_no_value')} /> | ||
| <QuotaDetailMetric label={t('usage_stats.credentials_quota_details_billing_remaining')} value={billingUsage.remaining ?? t('usage_stats.credentials_quota_details_no_value')} /> | ||
| </div> | ||
| ) : ( | ||
| <div className={styles.credentialQuotaDetailsGrid}> | ||
| <QuotaDetailMetric label={t('usage_stats.credentials_quota_details_current_tokens')} value={currentUsage?.tokens ?? t('usage_stats.credentials_quota_details_no_value')} /> | ||
| <QuotaDetailMetric label={t('usage_stats.credentials_quota_details_estimated_tokens')} value={estimatedUsage?.tokens ?? t('usage_stats.credentials_quota_details_no_value')} /> | ||
| <QuotaDetailMetric label={t('usage_stats.credentials_quota_details_current_cost')} value={currentUsage?.cost ?? t('usage_stats.credentials_quota_details_no_value')} /> | ||
| <QuotaDetailMetric label={t('usage_stats.credentials_quota_details_estimated_cost')} value={estimatedUsage?.cost ?? t('usage_stats.credentials_quota_details_no_value')} /> | ||
| </div> | ||
| )} | ||
| <div className={styles.credentialQuotaDetailsMeta}> | ||
| {percentLabel && <QuotaDetailLine label={t('usage_stats.credentials_quota_details_remaining')} value={percentLabel} />} | ||
| {resetLabel && <QuotaDetailLine label={t('usage_stats.credentials_quota_details_reset')} value={resetLabel} />} | ||
| </div> | ||
| </div> | ||
| </Modal> | ||
| ) | ||
| } | ||
|
|
||
| function QuotaDetailMetric({ label, value }: { label: string; value: string }) { | ||
| return ( | ||
| <span className={styles.credentialQuotaDetailsMetric}> | ||
| <span>{label}</span> | ||
| <strong>{value}</strong> | ||
| </span> | ||
| ) | ||
| } | ||
|
|
||
| function QuotaDetailLine({ label, value }: { label: string; value: string }) { | ||
| return ( | ||
| <div className={styles.credentialQuotaDetailsLine}> | ||
| <span>{label}</span> | ||
| <strong>{value}</strong> | ||
| </div> | ||
| ) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently,
selectedQuotastores a static snapshot of the quota object at the moment the user clicks the bar. If the row's quota is refreshed (either via background auto-refresh or manual update) while the modal is open, the modal will continue to display stale data.We can resolve this by dynamically finding the matching quota from
row.displayQuotasusing the selected quota's key. This ensures the modal always displays live, up-to-date information.