diff --git a/src/app/(dashboard)/dashboard/providers/page.tsx b/src/app/(dashboard)/dashboard/providers/page.tsx index e509322fa6..0921de4317 100644 --- a/src/app/(dashboard)/dashboard/providers/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/page.tsx @@ -123,6 +123,7 @@ export default function ProvidersPage() { missingCount: number; } | null>(null); const [repairingEnv, setRepairingEnv] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); const notify = useNotificationStore(); const t = useTranslations("providers"); const tc = useTranslations("common"); @@ -392,27 +393,32 @@ export default function ProvidersPage() { const oauthProviderEntries = filterConfiguredProviderEntries( buildMergedOAuthProviderEntries(OAUTH_PROVIDERS, FREE_PROVIDERS, getProviderStats), - showConfiguredOnly + showConfiguredOnly, + searchQuery ); const apiKeyProviderEntries = filterConfiguredProviderEntries( buildStaticProviderEntries("apikey", getProviderStats), - showConfiguredOnly + showConfiguredOnly, + searchQuery ); const webCookieProviderEntries = filterConfiguredProviderEntries( buildStaticProviderEntries("web-cookie", getProviderStats), - showConfiguredOnly + showConfiguredOnly, + searchQuery ); const searchProviderEntries = filterConfiguredProviderEntries( buildStaticProviderEntries("search", getProviderStats), - showConfiguredOnly + showConfiguredOnly, + searchQuery ); const audioProviderEntries = filterConfiguredProviderEntries( buildStaticProviderEntries("audio", getProviderStats), - showConfiguredOnly + showConfiguredOnly, + searchQuery ); const compatibleProviderEntries = filterConfiguredProviderEntries( @@ -439,7 +445,8 @@ export default function ProvidersPage() { toggleAuthType: "apikey" as const, })), ], - showConfiguredOnly + showConfiguredOnly, + searchQuery ); if (loading) { @@ -453,6 +460,31 @@ export default function ProvidersPage() { return (
+ {/* Search Bar */} +
+
+ + search + + setSearchQuery(e.target.value)} + placeholder={t("searchProviders")} + aria-label={t("searchProviders")} + className="pl-10 pr-10" + /> + {searchQuery && ( + + )} +
+
+ {/* Expiration Banner */} {expirations?.summary && (expirations.summary.expired > 0 || expirations.summary.expiringSoon > 0) && ( diff --git a/src/app/(dashboard)/dashboard/providers/providerPageUtils.ts b/src/app/(dashboard)/dashboard/providers/providerPageUtils.ts index d2f75617db..9b09433757 100644 --- a/src/app/(dashboard)/dashboard/providers/providerPageUtils.ts +++ b/src/app/(dashboard)/dashboard/providers/providerPageUtils.ts @@ -69,11 +69,26 @@ export function buildStaticProviderEntries( export function filterConfiguredProviderEntries( entries: ProviderEntry[], - showConfiguredOnly: boolean + showConfiguredOnly: boolean, + searchQuery?: string ): ProviderEntry[] { - if (!showConfiguredOnly) return entries; + let filtered = entries; - return entries.filter((entry) => Number(entry.stats?.total || 0) > 0); + if (showConfiguredOnly) { + filtered = filtered.filter((entry) => Number(entry.stats?.total || 0) > 0); + } + + if (searchQuery && searchQuery.trim()) { + const query = searchQuery.trim().toLowerCase(); + filtered = filtered.filter((entry) => { + const provider = entry.provider as Record; + const name = String(provider.name || "").toLowerCase(); + const id = entry.providerId.toLowerCase(); + return name.includes(query) || id.includes(query); + }); + } + + return filtered; } export function resolveDashboardProviderInfo( diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 9d00bc6591..ec00176783 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -1635,6 +1635,7 @@ "errorOccurred": "حدث خطأ. يرجى المحاولة مرة أخرى.", "modelStatus": "حالة النموذج", "showConfiguredOnly": "Configured only", + "searchProviders": "البحث عن موفري الخدمة...", "allModelsOperational": "جميع الموديلات شغالة", "modelsWithIssues": "{count} الطراز (النماذج) الذي به مشكلات", "allModelsNormal": "جميع النماذج تستجيب بشكل طبيعي.", diff --git a/src/i18n/messages/bg.json b/src/i18n/messages/bg.json index de2071474a..2c1c0d0bd3 100644 --- a/src/i18n/messages/bg.json +++ b/src/i18n/messages/bg.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Възникна грешка. Моля, опитайте отново.", "modelStatus": "Статус на модела", "showConfiguredOnly": "Configured only", + "searchProviders": "Търсете доставчици...", "allModelsOperational": "Всички модели работещи", "modelsWithIssues": "{count} модел(а) с проблеми", "allModelsNormal": "Всички модели реагират нормално.", diff --git a/src/i18n/messages/cs.json b/src/i18n/messages/cs.json index 1b8b4c6384..1a81088677 100644 --- a/src/i18n/messages/cs.json +++ b/src/i18n/messages/cs.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Došlo k chybě. Zkuste to prosím znovu.", "modelStatus": "Status modelu", "showConfiguredOnly": "Configured only", + "searchProviders": "Hledat poskytovatele...", "allModelsOperational": "Všechny modely funkční", "modelsWithIssues": "{count} model(y) s problémy", "allModelsNormal": "Všechny modely reagují normálně.", diff --git a/src/i18n/messages/da.json b/src/i18n/messages/da.json index fcf0034fd3..ae2cdaa225 100644 --- a/src/i18n/messages/da.json +++ b/src/i18n/messages/da.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Der opstod en fejl. Prøv venligst igen.", "modelStatus": "Modelstatus", "showConfiguredOnly": "Configured only", + "searchProviders": "Søg efter udbydere...", "allModelsOperational": "Alle modeller i drift", "modelsWithIssues": "{count} model(er) med problemer", "allModelsNormal": "Alle modeller reagerer normalt.", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index d60916651f..df2d6f7970 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.", "modelStatus": "Modellstatus", "showConfiguredOnly": "Configured only", + "searchProviders": "Anbieter suchen...", "allModelsOperational": "Alle Modelle betriebsbereit", "modelsWithIssues": "{count} Modell(e) mit Problemen", "allModelsNormal": "Alle Modelle reagieren normal.", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 4ed9b281af..e68d5d0e37 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -1682,6 +1682,7 @@ "errorOccurred": "An error occurred. Please try again.", "modelStatus": "Model Status", "showConfiguredOnly": "Configured only", + "searchProviders": "Search providers...", "allModelsOperational": "All models operational", "modelsWithIssues": "{count} model(s) with issues", "allModelsNormal": "All models are responding normally.", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 73b4ec07a7..c6416c544a 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Se produjo un error. Por favor inténtalo de nuevo.", "modelStatus": "Estado del modelo", "showConfiguredOnly": "Configured only", + "searchProviders": "Buscar proveedores...", "allModelsOperational": "Todos los modelos operativos.", "modelsWithIssues": "{count} modelo(s) con problemas", "allModelsNormal": "Todos los modelos están respondiendo normalmente.", diff --git a/src/i18n/messages/fi.json b/src/i18n/messages/fi.json index 08be63cae5..13db6ac4c9 100644 --- a/src/i18n/messages/fi.json +++ b/src/i18n/messages/fi.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Tapahtui virhe. Yritä uudelleen.", "modelStatus": "Mallin tila", "showConfiguredOnly": "Configured only", + "searchProviders": "Hae palveluntarjoajia...", "allModelsOperational": "Kaikki mallit toimivat", "modelsWithIssues": "{count} malli(t), joissa on ongelmia", "allModelsNormal": "Kaikki mallit reagoivat normaalisti.", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 1c2a0f7542..9d4ca65198 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Une erreur s'est produite. Veuillez réessayer.", "modelStatus": "Statut du modèle", "showConfiguredOnly": "Configured only", + "searchProviders": "Rechercher des fournisseurs...", "allModelsOperational": "Tous les modèles opérationnels", "modelsWithIssues": "{count} modèle(s) présentant des problèmes", "allModelsNormal": "Tous les modèles répondent normalement.", diff --git a/src/i18n/messages/he.json b/src/i18n/messages/he.json index d1b90146f3..a1cb3b2089 100644 --- a/src/i18n/messages/he.json +++ b/src/i18n/messages/he.json @@ -1635,6 +1635,7 @@ "errorOccurred": "אירעה שגיאה. אנא נסה שוב.", "modelStatus": "סטטוס דגם", "showConfiguredOnly": "Configured only", + "searchProviders": "חיפוש ספקים...", "allModelsOperational": "כל הדגמים מבצעיים", "modelsWithIssues": "{count} דגמים עם בעיות", "allModelsNormal": "כל הדגמים מגיבים כרגיל.", diff --git a/src/i18n/messages/hi.json b/src/i18n/messages/hi.json index 64ce703ef5..641b19cfbc 100644 --- a/src/i18n/messages/hi.json +++ b/src/i18n/messages/hi.json @@ -1635,6 +1635,7 @@ "errorOccurred": "एक त्रुटि हुई. कृपया पुन: प्रयास करें।", "modelStatus": "मॉडल स्थिति", "showConfiguredOnly": "Configured only", + "searchProviders": "प्रदाता खोजें...", "allModelsOperational": "सभी मॉडल क्रियाशील", "modelsWithIssues": "मुद्दों के साथ {count} मॉडल", "allModelsNormal": "सभी मॉडल सामान्य रूप से प्रतिक्रिया दे रहे हैं।", diff --git a/src/i18n/messages/hu.json b/src/i18n/messages/hu.json index 151d8f8832..452ea02973 100644 --- a/src/i18n/messages/hu.json +++ b/src/i18n/messages/hu.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Hiba történt. Kérjük, próbálja újra.", "modelStatus": "Modell állapota", "showConfiguredOnly": "Configured only", + "searchProviders": "Keressen szolgáltatók között...", "allModelsOperational": "Minden modell működőképes", "modelsWithIssues": "{count} problémákkal küzdő modell(ek).", "allModelsNormal": "Minden modell normálisan reagál.", diff --git a/src/i18n/messages/id.json b/src/i18n/messages/id.json index f0d07de0f1..261bb5c121 100644 --- a/src/i18n/messages/id.json +++ b/src/i18n/messages/id.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Terjadi kesalahan. Silakan coba lagi.", "modelStatus": "Status Model", "showConfiguredOnly": "Configured only", + "searchProviders": "Penyedia pencarian...", "allModelsOperational": "Semua model beroperasi", "modelsWithIssues": "{count} model yang bermasalah", "allModelsNormal": "Semua model merespons secara normal.", diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json index 3d73d0e614..21882de75f 100644 --- a/src/i18n/messages/it.json +++ b/src/i18n/messages/it.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Si è verificato un errore. Per favore riprova.", "modelStatus": "Stato del modello", "showConfiguredOnly": "Configured only", + "searchProviders": "Cerca fornitori...", "allModelsOperational": "Tutti i modelli operativi", "modelsWithIssues": "{count} modelli con problemi", "allModelsNormal": "Tutti i modelli rispondono normalmente.", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index e0c278c5f4..8e379b5a9b 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -1635,6 +1635,7 @@ "errorOccurred": "エラーが発生しました。もう一度試してください。", "modelStatus": "モデルステータス", "showConfiguredOnly": "Configured only", + "searchProviders": "プロバイダーを検索...", "allModelsOperational": "全モデル稼働中", "modelsWithIssues": "{count} モデルに問題があります", "allModelsNormal": "全機種正常に反応しております。", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index b32c5bcbee..331431b22a 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -1635,6 +1635,7 @@ "errorOccurred": "오류가 발생했습니다. 다시 시도해 주세요.", "modelStatus": "모델현황", "showConfiguredOnly": "Configured only", + "searchProviders": "공급자 검색...", "allModelsOperational": "모든 모델 작동 가능", "modelsWithIssues": "문제가 있는 {count} 모델", "allModelsNormal": "모든 모델이 정상적으로 반응하고 있습니다.", diff --git a/src/i18n/messages/ms.json b/src/i18n/messages/ms.json index 9638bf1b21..da47bc129f 100644 --- a/src/i18n/messages/ms.json +++ b/src/i18n/messages/ms.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Ralat berlaku. Sila cuba lagi.", "modelStatus": "Status Model", "showConfiguredOnly": "Configured only", + "searchProviders": "Cari pembekal...", "allModelsOperational": "Semua model beroperasi", "modelsWithIssues": "{count} model dengan isu", "allModelsNormal": "Semua model bertindak balas secara normal.", diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json index 3f74d1db21..a167ecc822 100644 --- a/src/i18n/messages/nl.json +++ b/src/i18n/messages/nl.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Er is een fout opgetreden. Probeer het opnieuw.", "modelStatus": "Modelstatus", "showConfiguredOnly": "Configured only", + "searchProviders": "Zoekaanbieders...", "allModelsOperational": "Alle modellen operationeel", "modelsWithIssues": "{count} model(len) met problemen", "allModelsNormal": "Alle modellen reageren normaal.", diff --git a/src/i18n/messages/no.json b/src/i18n/messages/no.json index bad36d619a..6f365f5c30 100644 --- a/src/i18n/messages/no.json +++ b/src/i18n/messages/no.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Det oppsto en feil. Vennligst prøv igjen.", "modelStatus": "Modellstatus", "showConfiguredOnly": "Configured only", + "searchProviders": "Søk etter leverandører...", "allModelsOperational": "Alle modeller i drift", "modelsWithIssues": "{count} modell(er) med problemer", "allModelsNormal": "Alle modellene reagerer normalt.", diff --git a/src/i18n/messages/phi.json b/src/i18n/messages/phi.json index 3499fcee65..5ead5bd771 100644 --- a/src/i18n/messages/phi.json +++ b/src/i18n/messages/phi.json @@ -1635,6 +1635,7 @@ "errorOccurred": "May naganap na error. Pakisubukang muli.", "modelStatus": "Katayuan ng Modelo", "showConfiguredOnly": "Configured only", + "searchProviders": "Maghanap ng mga provider...", "allModelsOperational": "Ang lahat ng mga modelo ay gumagana", "modelsWithIssues": "{count} (mga) modelong may mga isyu", "allModelsNormal": "Ang lahat ng mga modelo ay tumutugon nang normal.", diff --git a/src/i18n/messages/pl.json b/src/i18n/messages/pl.json index 17e5605fa5..e31063ad8b 100644 --- a/src/i18n/messages/pl.json +++ b/src/i18n/messages/pl.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Wystąpił błąd. Spróbuj ponownie.", "modelStatus": "Stan modelu", "showConfiguredOnly": "Configured only", + "searchProviders": "Wyszukaj dostawców...", "allModelsOperational": "Wszystkie modele sprawne", "modelsWithIssues": "{count} modele z problemami", "allModelsNormal": "Wszystkie modele reagują normalnie.", diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json index a2ec852344..25dfc0dc76 100644 --- a/src/i18n/messages/pt-BR.json +++ b/src/i18n/messages/pt-BR.json @@ -1687,6 +1687,7 @@ "errorOccurred": "Ocorreu um erro. Tente novamente.", "modelStatus": "Status dos Modelos", "showConfiguredOnly": "Configured only", + "searchProviders": "Buscar provedores...", "allModelsOperational": "Todos os modelos operacionais", "modelsWithIssues": "{count} modelo(s) com problemas", "allModelsNormal": "Todos os modelos estão respondendo normalmente.", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 6ab9e0282b..eb553354fc 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -1687,6 +1687,7 @@ "errorOccurred": "Ocorreu um erro. Por favor, tente novamente.", "modelStatus": "Status do modelo", "showConfiguredOnly": "Configured only", + "searchProviders": "Pesquisar fornecedores...", "allModelsOperational": "Todos os modelos operacionais", "modelsWithIssues": "{count} modelo(s) com problemas", "allModelsNormal": "Todos os modelos estão respondendo normalmente.", diff --git a/src/i18n/messages/ro.json b/src/i18n/messages/ro.json index 4f364bdbf8..b1ea5431d8 100644 --- a/src/i18n/messages/ro.json +++ b/src/i18n/messages/ro.json @@ -1635,6 +1635,7 @@ "errorOccurred": "A apărut o eroare. Vă rugăm să încercați din nou.", "modelStatus": "Stare model", "showConfiguredOnly": "Configured only", + "searchProviders": "Căutați furnizori...", "allModelsOperational": "Toate modelele sunt operaționale", "modelsWithIssues": "{count} model(e) cu probleme", "allModelsNormal": "Toate modelele răspund normal.", diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 6496d7e8ba..b94939cf71 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -1659,6 +1659,7 @@ "errorOccurred": "Произошла ошибка. Пожалуйста, попробуйте еще раз.", "modelStatus": "Статус модели", "showConfiguredOnly": "Только настроенные", + "searchProviders": "Поиск поставщиков...", "allModelsOperational": "Все модели в рабочем состоянии", "modelsWithIssues": "{count} модели с проблемами", "allModelsNormal": "Все модели реагируют нормально.", diff --git a/src/i18n/messages/sk.json b/src/i18n/messages/sk.json index 8cbf8424da..00040ffa2c 100644 --- a/src/i18n/messages/sk.json +++ b/src/i18n/messages/sk.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Vyskytla sa chyba. Skúste to znova.", "modelStatus": "Stav modelu", "showConfiguredOnly": "Configured only", + "searchProviders": "Hľadať poskytovateľov...", "allModelsOperational": "Všetky modely funkčné", "modelsWithIssues": "{count} modelov s problémami", "allModelsNormal": "Všetky modely reagujú normálne.", diff --git a/src/i18n/messages/sv.json b/src/i18n/messages/sv.json index 6f617db5f8..1bc1a51503 100644 --- a/src/i18n/messages/sv.json +++ b/src/i18n/messages/sv.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Ett fel uppstod. Försök igen.", "modelStatus": "Modellstatus", "showConfiguredOnly": "Configured only", + "searchProviders": "Sök efter leverantörer...", "allModelsOperational": "Alla modeller i drift", "modelsWithIssues": "{count} modell(er) med problem", "allModelsNormal": "Alla modeller svarar normalt.", diff --git a/src/i18n/messages/th.json b/src/i18n/messages/th.json index 4c832554d3..afaade7a46 100644 --- a/src/i18n/messages/th.json +++ b/src/i18n/messages/th.json @@ -1635,6 +1635,7 @@ "errorOccurred": "เกิดข้อผิดพลาด โปรดลองอีกครั้ง", "modelStatus": "สถานะโมเดล", "showConfiguredOnly": "Configured only", + "searchProviders": "ค้นหาผู้ให้บริการ...", "allModelsOperational": "ใช้งานได้ทุกรุ่น", "modelsWithIssues": "{count} โมเดลที่มีปัญหา", "allModelsNormal": "ทุกรุ่นตอบสนองได้ปกติ", diff --git a/src/i18n/messages/tr.json b/src/i18n/messages/tr.json index 5db4e7afc9..0f37e847af 100644 --- a/src/i18n/messages/tr.json +++ b/src/i18n/messages/tr.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Bir hata oluştu. Lütfen tekrar deneyin.", "modelStatus": "Model Durumu", "showConfiguredOnly": "Configured only", + "searchProviders": "Sağlayıcı ara...", "allModelsOperational": "Tüm modeller çalışır durumda", "modelsWithIssues": "{count} modelde sorun var", "allModelsNormal": "Tüm modeller normal şekilde yanıt veriyor.", diff --git a/src/i18n/messages/uk-UA.json b/src/i18n/messages/uk-UA.json index 61eeb1ee77..a72e4c5334 100644 --- a/src/i18n/messages/uk-UA.json +++ b/src/i18n/messages/uk-UA.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Сталася помилка. Спробуйте ще раз.", "modelStatus": "Статус моделі", "showConfiguredOnly": "Configured only", + "searchProviders": "Пошук постачальників...", "allModelsOperational": "Всі моделі робочі", "modelsWithIssues": "{count} моделі з проблемами", "allModelsNormal": "Усі моделі реагують нормально.", diff --git a/src/i18n/messages/vi.json b/src/i18n/messages/vi.json index 74b92b525f..fcc7df55c6 100644 --- a/src/i18n/messages/vi.json +++ b/src/i18n/messages/vi.json @@ -1635,6 +1635,7 @@ "errorOccurred": "Đã xảy ra lỗi. Vui lòng thử lại.", "modelStatus": "Tình trạng mẫu", "showConfiguredOnly": "Configured only", + "searchProviders": "Tìm kiếm nhà cung cấp...", "allModelsOperational": "Tất cả các mô hình hoạt động", "modelsWithIssues": "{count} mô hình có vấn đề", "allModelsNormal": "Tất cả các model đều phản hồi bình thường.", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 5f6f7e9655..e820d2e8d1 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -1731,6 +1731,7 @@ "errorOccurred": "发生错误。请再试一次。", "modelStatus": "模型状态", "showConfiguredOnly": "仅显示已配置", + "searchProviders": "搜索提供商...", "allModelsOperational": "所有模型运行正常", "modelsWithIssues": "{count} 有问题的模型", "allModelsNormal": "所有模型当前响应正常。", diff --git a/tests/unit/providers-page-utils.test.ts b/tests/unit/providers-page-utils.test.ts index 11bf8e49fc..f98ca74422 100644 --- a/tests/unit/providers-page-utils.test.ts +++ b/tests/unit/providers-page-utils.test.ts @@ -76,6 +76,112 @@ test("configured-only filter keeps only providers with saved connections", () => assert.equal(providerPageUtils.filterConfiguredProviderEntries(entries, false).length, 3); }); +test("search filter matches provider name and id case-insensitively", () => { + const entries = [ + { + providerId: "claude", + provider: { id: "claude", name: "Claude" }, + stats: { total: 2 }, + displayAuthType: "oauth", + toggleAuthType: "oauth", + }, + { + providerId: "openai", + provider: { id: "openai", name: "OpenAI" }, + stats: { total: 1 }, + displayAuthType: "oauth", + toggleAuthType: "oauth", + }, + { + providerId: "gemini", + provider: { id: "gemini", name: "Google Gemini" }, + stats: { total: 1 }, + displayAuthType: "oauth", + toggleAuthType: "oauth", + }, + ]; + + const byName = providerPageUtils.filterConfiguredProviderEntries(entries, false, "claude"); + assert.deepEqual( + byName.map((e) => e.providerId), + ["claude"] + ); + + const byNameCaseInsensitive = providerPageUtils.filterConfiguredProviderEntries( + entries, + false, + "OPENAI" + ); + assert.deepEqual( + byNameCaseInsensitive.map((e) => e.providerId), + ["openai"] + ); + + const byPartialName = providerPageUtils.filterConfiguredProviderEntries(entries, false, "google"); + assert.deepEqual( + byPartialName.map((e) => e.providerId), + ["gemini"] + ); + + const byId = providerPageUtils.filterConfiguredProviderEntries(entries, false, "gem"); + assert.deepEqual( + byId.map((e) => e.providerId), + ["gemini"] + ); + + const noMatch = providerPageUtils.filterConfiguredProviderEntries(entries, false, "xyz"); + assert.equal(noMatch.length, 0); + + const emptySearch = providerPageUtils.filterConfiguredProviderEntries(entries, false, ""); + assert.equal(emptySearch.length, 3); + + const whitespaceSearch = providerPageUtils.filterConfiguredProviderEntries(entries, false, " "); + assert.equal(whitespaceSearch.length, 3); +}); + +test("search and configured-only filters work together", () => { + const entries = [ + { + providerId: "claude", + provider: { id: "claude", name: "Claude" }, + stats: { total: 2 }, + displayAuthType: "oauth", + toggleAuthType: "oauth", + }, + { + providerId: "openai", + provider: { id: "openai", name: "OpenAI" }, + stats: { total: 0 }, + displayAuthType: "oauth", + toggleAuthType: "oauth", + }, + { + providerId: "gemini", + provider: { id: "gemini", name: "Google Gemini" }, + stats: { total: 1 }, + displayAuthType: "oauth", + toggleAuthType: "oauth", + }, + ]; + + const configuredAndSearched = providerPageUtils.filterConfiguredProviderEntries( + entries, + true, + "claude" + ); + assert.deepEqual( + configuredAndSearched.map((e) => e.providerId), + ["claude"] + ); + + const configuredButNoMatch = providerPageUtils.filterConfiguredProviderEntries( + entries, + true, + "openai" + ); + assert.equal(configuredButNoMatch.length, 0); +}); + test("configured-only preference parser only enables explicit true values", () => { assert.equal(providerPageStorage.parseConfiguredOnlyPreference("true"), true); assert.equal(providerPageStorage.parseConfiguredOnlyPreference("false"), false);