From 8c2d0df32c915b2afdd64b3cb2c66357e1b2c609 Mon Sep 17 00:00:00 2001 From: RGBadmin Date: Sat, 14 Mar 2026 11:01:06 +0800 Subject: [PATCH 1/2] feat(auth-files): display OAuth credential priority and add sorting - Show priority value on AuthFileCard when the credential has a priority field - Add sort dropdown (Default / A-Z Name / Priority) to the credentials list - Priority sort orders credentials from highest to lowest - Add i18n translations for zh-CN, en, and ru Co-Authored-By: Claude Opus 4.6 --- .../authFiles/components/AuthFileCard.tsx | 8 ++++ src/i18n/locales/en.json | 5 +++ src/i18n/locales/ru.json | 5 +++ src/i18n/locales/zh-CN.json | 5 +++ src/pages/AuthFilesPage.module.scss | 32 +++++++++++++++ src/pages/AuthFilesPage.tsx | 40 +++++++++++++++++-- 6 files changed, 91 insertions(+), 4 deletions(-) diff --git a/src/features/authFiles/components/AuthFileCard.tsx b/src/features/authFiles/components/AuthFileCard.tsx index 9e7b910..20aaae7 100644 --- a/src/features/authFiles/components/AuthFileCard.tsx +++ b/src/features/authFiles/components/AuthFileCard.tsx @@ -22,6 +22,7 @@ import { getTypeColor, getTypeLabel, isRuntimeOnlyAuthFile, + parsePriorityValue, resolveAuthFileStats, type QuotaProviderType, type ResolvedTheme, @@ -110,6 +111,8 @@ export function AuthFileCard(props: AuthFileCardProps) { const hasStatusWarning = Boolean(rawStatusMessage) && !HEALTHY_STATUS_MESSAGES.has(rawStatusMessage.toLowerCase()); + const priorityValue = parsePriorityValue(file.priority ?? file['priority']); + return (
{t('auth_files.file_modified')}: {formatModified(file)} + {priorityValue !== undefined && ( + + {t('auth_files.priority_display')}: {priorityValue} + + )}
{rawStatusMessage && hasStatusWarning && ( diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index c6cc4eb..af92324 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -495,6 +495,11 @@ "search_placeholder": "Filter by name, type, or provider", "problem_filter_label": "Problem Filter", "problem_filter_only": "Only show problematic credentials", + "sort_label": "Sort", + "sort_default": "Default", + "sort_az": "A-Z Name", + "sort_priority": "Priority", + "priority_display": "Priority", "page_size_label": "Per page", "page_size_unit": "items", "view_mode_paged": "Paged", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index bc7111e..c9a2564 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -495,6 +495,11 @@ "search_placeholder": "Фильтр по имени, типу или провайдеру", "problem_filter_label": "Фильтр проблем", "problem_filter_only": "Показывать только проблемные учётные данные", + "sort_label": "Сортировка", + "sort_default": "По умолчанию", + "sort_az": "A-Z Имя", + "sort_priority": "Приоритет", + "priority_display": "Приоритет", "page_size_label": "На странице", "page_size_unit": "элементов", "view_mode_paged": "Постранично", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index e01a95b..729134b 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -495,6 +495,11 @@ "search_placeholder": "输入名称、类型或提供方关键字", "problem_filter_label": "问题筛选", "problem_filter_only": "仅显示有问题凭证", + "sort_label": "排序", + "sort_default": "默认", + "sort_az": "A-Z 名称", + "sort_priority": "优先级", + "priority_display": "优先级", "page_size_label": "单页数量", "page_size_unit": "个/页", "view_mode_paged": "按页显示", diff --git a/src/pages/AuthFilesPage.module.scss b/src/pages/AuthFilesPage.module.scss index b284893..b446c31 100644 --- a/src/pages/AuthFilesPage.module.scss +++ b/src/pages/AuthFilesPage.module.scss @@ -632,6 +632,38 @@ border-bottom: 1px solid var(--border-color); } +.priorityBadge { + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 12px; + color: var(--text-secondary); + + .priorityValue { + font-weight: 600; + color: var(--text-primary); + font-variant-numeric: tabular-nums; + } +} + +.sortSelect { + padding: 8px 12px; + border: 1px solid var(--border-color); + border-radius: $radius-md; + background-color: var(--bg-primary); + color: var(--text-primary); + font-size: 14px; + cursor: pointer; + height: 38px; + box-sizing: border-box; + min-width: 140px; + + &:focus { + outline: none; + border-color: var(--primary-color); + } +} + .healthStatusMessage { font-size: 12px; color: var(--warning-text); diff --git a/src/pages/AuthFilesPage.tsx b/src/pages/AuthFilesPage.tsx index cda317b..30ec0a2 100644 --- a/src/pages/AuthFilesPage.tsx +++ b/src/pages/AuthFilesPage.tsx @@ -31,6 +31,7 @@ import { hasAuthFileStatusMessage, isRuntimeOnlyAuthFile, normalizeProviderKey, + parsePriorityValue, type QuotaProviderType, type ResolvedTheme, } from '@/features/authFiles/constants'; @@ -74,6 +75,7 @@ export function AuthFilesPage() { const [detailModalOpen, setDetailModalOpen] = useState(false); const [selectedFile, setSelectedFile] = useState(null); const [viewMode, setViewMode] = useState<'diagram' | 'list'>('list'); + const [sortMode, setSortMode] = useState<'default' | 'az' | 'priority'>('default'); const [batchActionBarVisible, setBatchActionBarVisible] = useState(false); const floatingBatchActionsRef = useRef(null); const batchActionAnimationRef = useRef(null); @@ -281,10 +283,25 @@ export function AuthFilesPage() { }); }, [filesMatchingProblemFilter, filter, search]); - const totalPages = Math.max(1, Math.ceil(filtered.length / pageSize)); + const sorted = useMemo(() => { + if (sortMode === 'default') return filtered; + const copy = [...filtered]; + if (sortMode === 'az') { + copy.sort((a, b) => a.name.localeCompare(b.name)); + } else if (sortMode === 'priority') { + copy.sort((a, b) => { + const pa = parsePriorityValue(a.priority ?? a['priority']) ?? 0; + const pb = parsePriorityValue(b.priority ?? b['priority']) ?? 0; + return pb - pa; // 高优先级排前面 + }); + } + return copy; + }, [filtered, sortMode]); + + const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize)); const currentPage = Math.min(page, totalPages); const start = (currentPage - 1) * pageSize; - const pageItems = filtered.slice(start, start + pageSize); + const pageItems = sorted.slice(start, start + pageSize); const selectablePageItems = useMemo( () => pageItems.filter((file) => !isRuntimeOnlyAuthFile(file)), [pageItems] @@ -559,6 +576,21 @@ export function AuthFilesPage() { }} /> +
+ + +
@@ -615,7 +647,7 @@ export function AuthFilesPage() {
)} - {!loading && filtered.length > pageSize && ( + {!loading && sorted.length > pageSize && (