mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-06-16 21:03:58 +08:00
feat(quota): enhance quota reset functionality with UI updates and styling improvements
This commit is contained in:
@@ -56,7 +56,6 @@ export function QuotaProgressBar({
|
||||
export interface QuotaRenderHelpers {
|
||||
styles: typeof styles;
|
||||
QuotaProgressBar: (props: QuotaProgressBarProps) => ReactElement;
|
||||
resetQuotaAction?: ReactNode;
|
||||
}
|
||||
|
||||
interface QuotaCardProps<TState extends QuotaStatusState> {
|
||||
@@ -149,27 +148,30 @@ export function QuotaCard<TState extends QuotaStatusState>({
|
||||
})}
|
||||
</div>
|
||||
) : quota ? (
|
||||
renderQuotaItems(quota, t, { styles, QuotaProgressBar, resetQuotaAction })
|
||||
renderQuotaItems(quota, t, { styles, QuotaProgressBar })
|
||||
) : (
|
||||
<div className={styles.quotaMessage}>{t(idleMessageKey)}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{onRefresh && quotaStatus !== 'idle' && (
|
||||
{(resetQuotaAction || (onRefresh && quotaStatus !== 'idle')) && (
|
||||
<div className={styles.quotaCardActions}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className={styles.quotaRefreshButton}
|
||||
onClick={onRefresh}
|
||||
disabled={!canRefresh || quotaLoading}
|
||||
loading={quotaLoading}
|
||||
title={t('auth_files.quota_refresh_hint')}
|
||||
>
|
||||
{!quotaLoading && <IconRefreshCw size={14} />}
|
||||
{t('auth_files.quota_refresh_single')}
|
||||
</Button>
|
||||
{resetQuotaAction}
|
||||
{onRefresh && quotaStatus !== 'idle' && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className={styles.quotaRefreshButton}
|
||||
onClick={onRefresh}
|
||||
disabled={!canRefresh || quotaLoading}
|
||||
loading={quotaLoading}
|
||||
title={t('auth_files.quota_refresh_hint')}
|
||||
>
|
||||
{!quotaLoading && <IconRefreshCw size={14} />}
|
||||
{t('auth_files.quota_refresh_single')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -360,7 +360,9 @@ export function QuotaSection<TState extends QuotaStatusState, TData>({
|
||||
const isResettingQuota = resettingQuotaName === item.name;
|
||||
const canUseQuotaAction =
|
||||
!disabled && !item.disabled && itemQuota?.status !== 'loading';
|
||||
const resetQuotaAction = config.resetQuota ? (
|
||||
const showResetQuotaAction =
|
||||
itemQuota !== undefined && Boolean(config.canResetQuota?.(itemQuota));
|
||||
const resetQuotaAction = config.resetQuota && showResetQuotaAction ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
|
||||
@@ -130,6 +130,7 @@ export interface QuotaConfig<TState, TData> {
|
||||
filterFn: (file: AuthFileItem) => boolean;
|
||||
fetchQuota: (file: AuthFileItem, t: TFunction) => Promise<TData>;
|
||||
resetQuota?: (file: AuthFileItem, t: TFunction) => Promise<TData>;
|
||||
canResetQuota?: (quota: TState) => boolean;
|
||||
storeSelector: (state: QuotaStore) => Record<string, TState>;
|
||||
storeSetter: keyof QuotaStore;
|
||||
buildLoadingState: () => TState;
|
||||
@@ -857,10 +858,6 @@ const renderCodexItems = (
|
||||
const planLabel = getPlanLabel(planType);
|
||||
const isPremiumPlan = PREMIUM_CODEX_PLAN_TYPES.has(normalizePlanType(planType) ?? '');
|
||||
const expiryLabel = subscriptionActiveUntil ? formatDateTimeValue(subscriptionActiveUntil) : '';
|
||||
const resetQuotaAction =
|
||||
rateLimitResetCreditsAvailableCount !== null && rateLimitResetCreditsAvailableCount > 0
|
||||
? helpers.resetQuotaAction
|
||||
: null;
|
||||
const nodes: ReactNode[] = [];
|
||||
|
||||
if (planLabel || expiryLabel || rateLimitResetCreditsAvailableCount !== null) {
|
||||
@@ -922,16 +919,6 @@ const renderCodexItems = (
|
||||
nodes.push(h('div', { key: 'plan', className: styleMap.codexPlan }, ...planNodes));
|
||||
}
|
||||
|
||||
if (resetQuotaAction) {
|
||||
nodes.push(
|
||||
h(
|
||||
'div',
|
||||
{ key: 'reset-credits-action', className: styleMap.quotaInlineActions },
|
||||
resetQuotaAction
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (windows.length === 0) {
|
||||
nodes.push(
|
||||
h('div', { key: 'empty', className: styleMap.quotaMessage }, t('codex_quota.empty_windows'))
|
||||
@@ -1354,6 +1341,7 @@ export const CODEX_CONFIG: QuotaConfig<
|
||||
filterFn: (file) => isCodexFile(file) && !isDisabledAuthFile(file),
|
||||
fetchQuota: fetchCodexQuota,
|
||||
resetQuota: resetCodexQuota,
|
||||
canResetQuota: (quota) => (quota.rateLimitResetCreditsAvailableCount ?? 0) > 0,
|
||||
storeSelector: (state) => state.codexQuota,
|
||||
storeSetter: 'setCodexQuota',
|
||||
buildLoadingState: () => ({ status: 'loading', windows: [] }),
|
||||
|
||||
@@ -155,20 +155,22 @@ export function AuthFileQuotaSection(props: AuthFileQuotaSectionProps) {
|
||||
const config = getQuotaConfig(quotaType) as unknown as {
|
||||
i18nPrefix: string;
|
||||
resetQuota?: (file: AuthFileItem, t: TFunction) => Promise<unknown>;
|
||||
canResetQuota?: (quota: unknown) => boolean;
|
||||
renderQuotaItems: (quota: unknown, t: TFunction, helpers: unknown) => unknown;
|
||||
};
|
||||
|
||||
const quotaStatus = quota?.status ?? 'idle';
|
||||
const canRefreshQuota = !disableControls && !file.disabled && !resettingQuota;
|
||||
const canResetQuota = canRefreshQuota && quotaStatus !== 'loading';
|
||||
const resetQuotaAction = config.resetQuota ? (
|
||||
const canUseResetQuota = canRefreshQuota && quotaStatus !== 'loading';
|
||||
const showResetQuotaAction = quota !== undefined && Boolean(config.canResetQuota?.(quota));
|
||||
const resetQuotaAction = config.resetQuota && showResetQuotaAction ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
className={styles.quotaResetCreditButton}
|
||||
onClick={() => resetQuotaForFile()}
|
||||
disabled={!canResetQuota}
|
||||
disabled={!canUseResetQuota}
|
||||
loading={resettingQuota}
|
||||
title={t('codex_quota.reset_button')}
|
||||
aria-label={t('codex_quota.reset_button')}
|
||||
@@ -206,11 +208,13 @@ export function AuthFileQuotaSection(props: AuthFileQuotaSectionProps) {
|
||||
(config.renderQuotaItems(quota, t, {
|
||||
styles,
|
||||
QuotaProgressBar,
|
||||
resetQuotaAction,
|
||||
}) as ReactNode)
|
||||
) : (
|
||||
<div className={styles.quotaMessage}>{t(`${config.i18nPrefix}.idle`)}</div>
|
||||
)}
|
||||
{quotaStatus !== 'idle' && resetQuotaAction && (
|
||||
<div className={styles.quotaCardActions}>{resetQuotaAction}</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -640,12 +640,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.quotaInlineActions {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding-top: $spacing-xs;
|
||||
}
|
||||
|
||||
.quotaResetCreditButton:global(.btn.btn-sm) {
|
||||
border-radius: 999px;
|
||||
padding-inline: 12px;
|
||||
@@ -658,6 +652,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.quotaCardActions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: $spacing-xs;
|
||||
flex-wrap: wrap;
|
||||
padding-top: $spacing-xs;
|
||||
}
|
||||
|
||||
.quotaError {
|
||||
font-size: 12px;
|
||||
color: var(--danger-color);
|
||||
|
||||
@@ -392,15 +392,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.quotaInlineActions {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
padding-top: $spacing-xs;
|
||||
}
|
||||
|
||||
.quotaCardActions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: $spacing-xs;
|
||||
flex-wrap: wrap;
|
||||
padding-top: $spacing-xs;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user