diff --git a/client/src/components/Admin/AddonManager.test.tsx b/client/src/components/Admin/AddonManager.test.tsx index 1c9a1e571..77b5d6d34 100644 --- a/client/src/components/Admin/AddonManager.test.tsx +++ b/client/src/components/Admin/AddonManager.test.tsx @@ -712,4 +712,33 @@ describe('AddonManager', () => { expect(roadtrip.querySelector('svg.lucide-car-front')).toBeInTheDocument(); expect(tours.querySelector('svg.lucide-puzzle')).not.toBeInTheDocument(); }); + + it('FE-ADMIN-ADDON-045: finds an addon by name or description and shows the ones on or off', async () => { + const user = userEvent.setup(); + server.use( + addonsRoute([ + buildAddon({ id: 'x-lists', name: 'Todo List', description: 'Track tasks', type: 'trip', enabled: true }), + buildAddon({ id: 'x-costs', name: 'Budget', description: 'Split costs', type: 'trip', enabled: false }), + buildAddon({ id: 'x-holiday', name: 'Vacay', description: 'Holiday planner', type: 'global', enabled: true }), + ]) + ); + render(); + await screen.findByText('Todo List'); + + await user.type(screen.getByRole('searchbox', { name: 'Search addons' }), 'costs'); + expect(screen.getByText('Budget')).toBeInTheDocument(); + expect(screen.queryByText('Todo List')).toBeNull(); + expect(screen.queryByText('Vacay')).toBeNull(); + + await user.clear(screen.getByRole('searchbox', { name: 'Search addons' })); + await user.click(screen.getByRole('button', { name: 'On' })); + expect(screen.getByText('Todo List')).toBeInTheDocument(); + expect(screen.getByText('Vacay')).toBeInTheDocument(); + expect(screen.queryByText('Budget')).toBeNull(); + + await user.click(screen.getByRole('button', { name: 'Off' })); + await user.type(screen.getByRole('searchbox', { name: 'Search addons' }), 'nothing like this'); + expect(screen.getByText('No addon matches the search or the filter.')).toBeInTheDocument(); + }); }); + diff --git a/client/src/components/Admin/AddonManager.tsx b/client/src/components/Admin/AddonManager.tsx index af0e36f6f..93e92a465 100644 --- a/client/src/components/Admin/AddonManager.tsx +++ b/client/src/components/Admin/AddonManager.tsx @@ -1,7 +1,7 @@ -import type { ComponentType } from 'react' +import { useState, type ComponentType } from 'react' import { useTranslation } from '../../i18n' import { useIsDark } from '../../hooks/useIsDark' -import { Puzzle, ListChecks, Wallet, FileText, CalendarDays, Globe, Briefcase, Image, Terminal, Link2, Compass, BookOpen, Sparkles, Luggage, Plane, Bookmark, Users, Loader2, Mountain, CarFront } from 'lucide-react' +import { Puzzle, ListChecks, Wallet, FileText, CalendarDays, Globe, Briefcase, Image, Terminal, Link2, Compass, BookOpen, Sparkles, Luggage, Plane, Bookmark, Users, Loader2, Mountain, CarFront, Search } from 'lucide-react' import EmptyState from '../shared/EmptyState' import DawarichIcon from '../shared/DawarichIcon' import AirTrailIcon from '../shared/AirTrailIcon' @@ -76,6 +76,8 @@ export default function AddonManager({ bagTrackingEnabled, onToggleBagTracking, toggleToursWanderer, mapSwitches, } = useAddonManager() + const [query, setQuery] = useState('') + const [view, setView] = useState('all') if (loading) { return ( @@ -194,10 +196,10 @@ export default function AddonManager({ bagTrackingEnabled, onToggleBagTracking, ) } - /* One column per type, side by side, instead of three stacked sections. Stacked, each - section opened its own set of columns and the tall tiles (Collab, Journey) set a - section's height while its neighbours ran out early. Side by side the column's - head carries the type, so the tiles need no badge of their own. */ + /* A card per type, the way every other admin tab sets its cards: Trip in the + left column, Global and Integration in the right one, each card with the + dialogs' head band carrying the type, its count and what it means. Above + them one card says how many are on and finds an addon by name. */ const groups = [ { key: 'trip', addons: tripAddons }, { key: 'global', addons: globalAddons }, @@ -214,68 +216,145 @@ export default function AddonManager({ bagTrackingEnabled, onToggleBagTracking, ) - return ( - 0 ? ( - - - 0 ? 'success' : 'neutral'}>{enabledCount}/{totalCount} - - - ) : undefined} - > - {addons.length === 0 ? ( - - ) : ( -
- {groups.map(g => { - const meta = TYPE_META[g.key] - return ( -
- -
- {/* The AI addons share a connection and a settings dialog, so they head the column as one tile. */} - {g.key === 'integration' && aiAddons.length > 0 && } - {g.addons.map(tile)} -
-
- ) - })} + /** Whether an addon is shown under the search and the filter. */ + const shown = (addon: Addon) => { + if (view === 'on' && !addon.enabled) return false + if (view === 'off' && addon.enabled) return false + const q = query.trim().toLocaleLowerCase() + if (!q) return true + const label = getAddonLabel(t, addon) + return `${label.name} ${label.description ?? ''}`.toLocaleLowerCase().includes(q) + } + + const card = (g: (typeof groups)[number]) => { + const meta = TYPE_META[g.key] + const all = g.key === 'integration' ? [...aiAddons, ...g.addons] : g.addons + const visible = g.addons.filter(shown) + const ai = g.key === 'integration' && aiAddons.some(shown) + if (!visible.length && !ai) return null + const on = all.filter(a => a.enabled).length + return ( + + 0 ? 'success' : 'neutral'}>{on}/{all.length} + + )} + bodyClassName="p-3" + > +
+ {/* The AI addons share a connection and a settings dialog, so they head the card as one tile. */} + {ai && } + {visible.map(tile)}
- )} -
+ + ) + } + + const [left, ...right] = groups + const cards = { left: left ? card(left) : null, right: right.map(card).filter(Boolean) } + + return ( + <> + 0 ? ( + + + 0 ? 'success' : 'neutral'}>{enabledCount}/{totalCount} + + + ) : undefined} + > + {addons.length === 0 + ? + : ({ key: g.key, addons: g.key === 'integration' ? [...aiAddons, ...g.addons] : g.addons }))} query={query} onQuery={setQuery} view={view} onView={setView} t={t} />} + + + {addons.length > 0 && (cards.left || cards.right.length > 0 ? ( +
+
{cards.left}
+
{cards.right}
+
+ ) : ( +

+ {t('admin.addons.noMatch')} +

+ ))} + ) } -/** The head band of one type's column: icon tile, the type, its count and what it means. */ -function GroupHead({ icon: Icon, label, hint, addons, t }: { - icon: ComponentType<{ size?: number; className?: string; strokeWidth?: number }> - label: string - hint: string - addons: Addon[] +type AddonView = 'all' | 'on' | 'off' + +/** + * The head card's body: a tile per type with how many of it are on, then a + * search over the addons' names and descriptions and a switch between all, + * the ones on and the ones off. + */ +function AddonToolbar({ groups, query, onQuery, view, onView, t }: { + groups: { key: string; addons: Addon[] }[] + query: string + onQuery: (q: string) => void + view: AddonView + onView: (v: AddonView) => void t: (key: string, params?: Record) => string }) { - const enabled = addons.filter(a => a.enabled).length return ( -
- - - -
-
-

{label}

- - - {enabled}/{addons.length} - - -
- {/* Says what the type means: the only place that still explains it. */} -

{hint}

+ <> +
+ {groups.map(g => { + const meta = TYPE_META[g.key] + const Icon = meta.icon + const on = g.addons.filter(a => a.enabled).length + return ( +
+ + + + + {t(meta.labelKey)} + {t('admin.addons.group.count', { enabled: on, total: g.addons.length })} + + 0 ? 'text-content' : 'text-content-faint'}`} style={fs(18, 'body')} aria-hidden>{on} +
+ ) + })}
-
+
+ +
+ {(['all', 'on', 'off'] as const).map(v => ( + + ))} +
+
+ ) } diff --git a/shared/src/i18n/ar/admin.ts b/shared/src/i18n/ar/admin.ts index 700251a76..b526afd14 100644 --- a/shared/src/i18n/ar/admin.ts +++ b/shared/src/i18n/ar/admin.ts @@ -708,6 +708,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'تم تحديث الإضافة', 'admin.addons.toast.error': 'فشل تحديث الإضافة', 'admin.addons.group.count': '{enabled} من {total} مُفعّل', + 'admin.addons.search': 'البحث في الإضافات', + 'admin.addons.filter': 'عرض', + 'admin.addons.filter.all': 'الكل', + 'admin.addons.filter.on': 'مفعّلة', + 'admin.addons.filter.off': 'معطّلة', + 'admin.addons.noMatch': 'لا توجد إضافة تطابق البحث أو عامل التصفية.', 'admin.addons.subOptions': '{count} خيار', 'admin.addons.subOptions.zero': 'لا توجد خيارات', 'admin.addons.subOptions.one': 'خيار واحد', diff --git a/shared/src/i18n/az/admin.ts b/shared/src/i18n/az/admin.ts index 3d6d7396c..7f64184b1 100644 --- a/shared/src/i18n/az/admin.ts +++ b/shared/src/i18n/az/admin.ts @@ -749,6 +749,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Əlavə yeniləndi', 'admin.addons.toast.error': 'Əlavəni yeniləmək mümkün olmadı', 'admin.addons.group.count': '{total} əlavədən {enabled} aktivdir', + 'admin.addons.search': 'Əlavələrdə axtar', + 'admin.addons.filter': 'Göstər', + 'admin.addons.filter.all': 'Hamısı', + 'admin.addons.filter.on': 'Aktiv', + 'admin.addons.filter.off': 'Deaktiv', + 'admin.addons.noMatch': 'Axtarışa və ya filtrə uyğun əlavə yoxdur.', 'admin.addons.subOptions': '{count} seçim', 'admin.addons.subOptions.one': '{count} seçim', 'admin.addons.subOptionsOn': '{count} aktiv', diff --git a/shared/src/i18n/br/admin.ts b/shared/src/i18n/br/admin.ts index 495431d80..ace5e1a95 100644 --- a/shared/src/i18n/br/admin.ts +++ b/shared/src/i18n/br/admin.ts @@ -657,6 +657,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Complemento atualizado', 'admin.addons.toast.error': 'Falha ao atualizar complemento', 'admin.addons.group.count': '{enabled} de {total} ativados', + 'admin.addons.search': 'Pesquisar complementos', + 'admin.addons.filter': 'Mostrar', + 'admin.addons.filter.all': 'Todos', + 'admin.addons.filter.on': 'Ativos', + 'admin.addons.filter.off': 'Inativos', + 'admin.addons.noMatch': 'Nenhum complemento corresponde à pesquisa ou ao filtro.', 'admin.addons.subOptions': '{count} opções', 'admin.addons.subOptions.one': '{count} opção', 'admin.addons.subOptionsOn': '{count} ativas', diff --git a/shared/src/i18n/ca/admin.ts b/shared/src/i18n/ca/admin.ts index 38cf64a9a..77f28b689 100644 --- a/shared/src/i18n/ca/admin.ts +++ b/shared/src/i18n/ca/admin.ts @@ -264,6 +264,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Complement actualitzat', 'admin.addons.toast.error': "No s'ha pogut actualitzar el complement", 'admin.addons.group.count': '{enabled} de {total} actius', + 'admin.addons.search': 'Cerca complements', + 'admin.addons.filter': 'Mostra', + 'admin.addons.filter.all': 'Tots', + 'admin.addons.filter.on': 'Actius', + 'admin.addons.filter.off': 'Inactius', + 'admin.addons.noMatch': 'Cap complement coincideix amb la cerca o el filtre.', 'admin.addons.subOptions': '{count} opcions', 'admin.addons.subOptions.one': '{count} opció', 'admin.addons.subOptionsOn': '{count} actives', diff --git a/shared/src/i18n/cs/admin.ts b/shared/src/i18n/cs/admin.ts index 6dba21c38..cd5e8b39f 100644 --- a/shared/src/i18n/cs/admin.ts +++ b/shared/src/i18n/cs/admin.ts @@ -657,6 +657,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Doplněk byl aktualizován', 'admin.addons.toast.error': 'Aktualizace doplňku se nezdařila', 'admin.addons.group.count': '{enabled} z {total} zapnuto', + 'admin.addons.search': 'Hledat doplňky', + 'admin.addons.filter': 'Zobrazit', + 'admin.addons.filter.all': 'Vše', + 'admin.addons.filter.on': 'Zapnuté', + 'admin.addons.filter.off': 'Vypnuté', + 'admin.addons.noMatch': 'Hledání ani filtru neodpovídá žádný doplněk.', 'admin.addons.subOptions': '{count} možností', 'admin.addons.subOptions.one': '{count} možnost', 'admin.addons.subOptions.few': '{count} možnosti', diff --git a/shared/src/i18n/de/admin.ts b/shared/src/i18n/de/admin.ts index ebd5c35d1..174fa7c2e 100644 --- a/shared/src/i18n/de/admin.ts +++ b/shared/src/i18n/de/admin.ts @@ -665,6 +665,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Addon aktualisiert', 'admin.addons.toast.error': 'Addon konnte nicht aktualisiert werden', 'admin.addons.group.count': '{enabled} von {total} aktiviert', + 'admin.addons.search': 'Addons durchsuchen', + 'admin.addons.filter': 'Anzeigen', + 'admin.addons.filter.all': 'Alle', + 'admin.addons.filter.on': 'An', + 'admin.addons.filter.off': 'Aus', + 'admin.addons.noMatch': 'Kein Addon passt zur Suche oder zum Filter.', 'admin.addons.subOptions': '{count} Optionen', 'admin.addons.subOptions.one': '{count} Option', 'admin.addons.subOptionsOn': '{count} aktiv', diff --git a/shared/src/i18n/en/admin.ts b/shared/src/i18n/en/admin.ts index 52c6c635f..7c526f831 100644 --- a/shared/src/i18n/en/admin.ts +++ b/shared/src/i18n/en/admin.ts @@ -710,6 +710,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Addon updated', 'admin.addons.toast.error': 'Failed to update addon', 'admin.addons.group.count': '{enabled} of {total} enabled', + 'admin.addons.search': 'Search addons', + 'admin.addons.filter': 'Show', + 'admin.addons.filter.all': 'All', + 'admin.addons.filter.on': 'On', + 'admin.addons.filter.off': 'Off', + 'admin.addons.noMatch': 'No addon matches the search or the filter.', 'admin.addons.subOptions': '{count} options', 'admin.addons.subOptions.one': '{count} option', 'admin.addons.subOptionsOn': '{count} on', diff --git a/shared/src/i18n/es/admin.ts b/shared/src/i18n/es/admin.ts index 396e3b5d8..e931816a9 100644 --- a/shared/src/i18n/es/admin.ts +++ b/shared/src/i18n/es/admin.ts @@ -646,6 +646,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Complemento actualizado', 'admin.addons.toast.error': 'No se pudo actualizar el complemento', 'admin.addons.group.count': '{enabled} de {total} activados', + 'admin.addons.search': 'Buscar complementos', + 'admin.addons.filter': 'Mostrar', + 'admin.addons.filter.all': 'Todos', + 'admin.addons.filter.on': 'Activos', + 'admin.addons.filter.off': 'Inactivos', + 'admin.addons.noMatch': 'Ningún complemento coincide con la búsqueda o el filtro.', 'admin.addons.subOptions': '{count} opciones', 'admin.addons.subOptions.one': '{count} opción', 'admin.addons.subOptionsOn': '{count} activas', diff --git a/shared/src/i18n/et/admin.ts b/shared/src/i18n/et/admin.ts index 1468e6982..dedae4702 100644 --- a/shared/src/i18n/et/admin.ts +++ b/shared/src/i18n/et/admin.ts @@ -729,6 +729,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Lisamoodul uuendatud', 'admin.addons.toast.error': 'Lisamooduli uuendamine ebaõnnestus', 'admin.addons.group.count': 'Lubatud {enabled}/{total}', + 'admin.addons.search': 'Otsi lisasid', + 'admin.addons.filter': 'Näita', + 'admin.addons.filter.all': 'Kõik', + 'admin.addons.filter.on': 'Sees', + 'admin.addons.filter.off': 'Väljas', + 'admin.addons.noMatch': 'Otsingule ega filtrile ei vasta ükski lisa.', 'admin.addons.subOptions': '{count} valikut', 'admin.addons.subOptions.one': '{count} valik', 'admin.addons.subOptionsOn': '{count} lubatud', diff --git a/shared/src/i18n/fr/admin.ts b/shared/src/i18n/fr/admin.ts index 757a0125d..dd35664a6 100644 --- a/shared/src/i18n/fr/admin.ts +++ b/shared/src/i18n/fr/admin.ts @@ -671,6 +671,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Extension mise à jour', 'admin.addons.toast.error': "Échec de la mise à jour de l'extension", 'admin.addons.group.count': '{enabled} sur {total} activés', + 'admin.addons.search': 'Rechercher des modules', + 'admin.addons.filter': 'Afficher', + 'admin.addons.filter.all': 'Tous', + 'admin.addons.filter.on': 'Activés', + 'admin.addons.filter.off': 'Désactivés', + 'admin.addons.noMatch': 'Aucun module ne correspond à la recherche ou au filtre.', 'admin.addons.subOptions': '{count} options', // same-as-en 'admin.addons.subOptions.one': '{count} option', // same-as-en 'admin.addons.subOptionsOn': '{count} activées', diff --git a/shared/src/i18n/gr/admin.ts b/shared/src/i18n/gr/admin.ts index c64e746b6..71f68ac78 100644 --- a/shared/src/i18n/gr/admin.ts +++ b/shared/src/i18n/gr/admin.ts @@ -733,6 +733,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Το πρόσθετο ενημερώθηκε', 'admin.addons.toast.error': 'Αποτυχία ενημέρωσης προσθέτου', 'admin.addons.group.count': '{enabled} από {total} ενεργοποιημένα', + 'admin.addons.search': 'Αναζήτηση πρόσθετων', + 'admin.addons.filter': 'Εμφάνιση', + 'admin.addons.filter.all': 'Όλα', + 'admin.addons.filter.on': 'Ενεργά', + 'admin.addons.filter.off': 'Ανενεργά', + 'admin.addons.noMatch': 'Κανένα πρόσθετο δεν ταιριάζει με την αναζήτηση ή το φίλτρο.', 'admin.addons.subOptions': '{count} επιλογές', 'admin.addons.subOptions.one': '{count} επιλογή', 'admin.addons.subOptionsOn': '{count} ενεργές', diff --git a/shared/src/i18n/hu/admin.ts b/shared/src/i18n/hu/admin.ts index 06dffc26d..18b3eee3b 100644 --- a/shared/src/i18n/hu/admin.ts +++ b/shared/src/i18n/hu/admin.ts @@ -671,6 +671,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Bővítmény frissítve', 'admin.addons.toast.error': 'Nem sikerült frissíteni a bővítményt', 'admin.addons.group.count': '{total} közül {enabled} bekapcsolva', + 'admin.addons.search': 'Bővítmények keresése', + 'admin.addons.filter': 'Megjelenítés', + 'admin.addons.filter.all': 'Mind', + 'admin.addons.filter.on': 'Be', + 'admin.addons.filter.off': 'Ki', + 'admin.addons.noMatch': 'Egy bővítmény sem felel meg a keresésnek vagy a szűrőnek.', 'admin.addons.subOptions': '{count} beállítás', 'admin.addons.subOptions.one': '{count} beállítás', 'admin.addons.subOptionsOn': '{count} bekapcsolva', diff --git a/shared/src/i18n/id/admin.ts b/shared/src/i18n/id/admin.ts index 3a46e7489..da849ec77 100644 --- a/shared/src/i18n/id/admin.ts +++ b/shared/src/i18n/id/admin.ts @@ -711,6 +711,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Addon diperbarui', 'admin.addons.toast.error': 'Gagal memperbarui addon', 'admin.addons.group.count': '{enabled} dari {total} aktif', + 'admin.addons.search': 'Cari add-on', + 'admin.addons.filter': 'Tampilkan', + 'admin.addons.filter.all': 'Semua', + 'admin.addons.filter.on': 'Aktif', + 'admin.addons.filter.off': 'Nonaktif', + 'admin.addons.noMatch': 'Tidak ada add-on yang cocok dengan pencarian atau filter.', 'admin.addons.subOptions': '{count} opsi', 'admin.addons.subOptionsOn': '{count} aktif', 'admin.toursWanderer.title': 'wanderer', diff --git a/shared/src/i18n/it/admin.ts b/shared/src/i18n/it/admin.ts index c23bf7d8d..6360cf808 100644 --- a/shared/src/i18n/it/admin.ts +++ b/shared/src/i18n/it/admin.ts @@ -665,6 +665,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Modulo aggiornato', 'admin.addons.toast.error': 'Impossibile aggiornare il modulo', 'admin.addons.group.count': '{enabled} di {total} attivi', + 'admin.addons.search': 'Cerca componenti aggiuntivi', + 'admin.addons.filter': 'Mostra', + 'admin.addons.filter.all': 'Tutti', + 'admin.addons.filter.on': 'Attivi', + 'admin.addons.filter.off': 'Disattivi', + 'admin.addons.noMatch': 'Nessun componente corrisponde alla ricerca o al filtro.', 'admin.addons.subOptions': '{count} opzioni', 'admin.addons.subOptions.one': '{count} opzione', 'admin.addons.subOptionsOn': '{count} attive', diff --git a/shared/src/i18n/ja/admin.ts b/shared/src/i18n/ja/admin.ts index 4ea40d6fb..81341024a 100644 --- a/shared/src/i18n/ja/admin.ts +++ b/shared/src/i18n/ja/admin.ts @@ -674,6 +674,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'アドオンを更新しました', 'admin.addons.toast.error': 'アドオンの更新に失敗しました', 'admin.addons.group.count': '{total} 件中 {enabled} 件が有効', + 'admin.addons.search': 'アドオンを検索', + 'admin.addons.filter': '表示', + 'admin.addons.filter.all': 'すべて', + 'admin.addons.filter.on': 'オン', + 'admin.addons.filter.off': 'オフ', + 'admin.addons.noMatch': '検索またはフィルターに一致するアドオンはありません。', 'admin.addons.subOptions': '{count}件のオプション', 'admin.addons.subOptionsOn': '{count}件が有効', 'admin.toursWanderer.title': 'wanderer', diff --git a/shared/src/i18n/ko/admin.ts b/shared/src/i18n/ko/admin.ts index b6cdd101b..cafe6322f 100644 --- a/shared/src/i18n/ko/admin.ts +++ b/shared/src/i18n/ko/admin.ts @@ -673,6 +673,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': '애드온이 업데이트되었습니다', 'admin.addons.toast.error': '애드온 업데이트 실패', 'admin.addons.group.count': '{total}개 중 {enabled}개 활성화됨', + 'admin.addons.search': '애드온 검색', + 'admin.addons.filter': '표시', + 'admin.addons.filter.all': '전체', + 'admin.addons.filter.on': '켜짐', + 'admin.addons.filter.off': '꺼짐', + 'admin.addons.noMatch': '검색 또는 필터와 일치하는 애드온이 없습니다.', 'admin.addons.subOptions': '옵션 {count}개', 'admin.addons.subOptionsOn': '{count}개 켜짐', 'admin.toursWanderer.title': 'wanderer', diff --git a/shared/src/i18n/nl/admin.ts b/shared/src/i18n/nl/admin.ts index 0f14b4615..662679536 100644 --- a/shared/src/i18n/nl/admin.ts +++ b/shared/src/i18n/nl/admin.ts @@ -664,6 +664,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Add-on bijgewerkt', 'admin.addons.toast.error': 'Add-on bijwerken mislukt', 'admin.addons.group.count': '{enabled} van {total} ingeschakeld', + 'admin.addons.search': 'Add-ons zoeken', + 'admin.addons.filter': 'Tonen', + 'admin.addons.filter.all': 'Alle', + 'admin.addons.filter.on': 'Aan', + 'admin.addons.filter.off': 'Uit', + 'admin.addons.noMatch': 'Geen add-on past bij de zoekopdracht of het filter.', 'admin.addons.subOptions': '{count} opties', 'admin.addons.subOptions.one': '{count} optie', 'admin.addons.subOptionsOn': '{count} aan', diff --git a/shared/src/i18n/pl/admin.ts b/shared/src/i18n/pl/admin.ts index 928861958..e2176fa5e 100644 --- a/shared/src/i18n/pl/admin.ts +++ b/shared/src/i18n/pl/admin.ts @@ -680,6 +680,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Dodatek został zaktualizowany', 'admin.addons.toast.error': 'Nie udało się zaktualizować dodatku', 'admin.addons.group.count': '{enabled} z {total} włączonych', + 'admin.addons.search': 'Szukaj dodatków', + 'admin.addons.filter': 'Pokaż', + 'admin.addons.filter.all': 'Wszystkie', + 'admin.addons.filter.on': 'Włączone', + 'admin.addons.filter.off': 'Wyłączone', + 'admin.addons.noMatch': 'Żaden dodatek nie pasuje do wyszukiwania ani filtra.', 'admin.addons.subOptions': '{count} opcji', 'admin.addons.subOptions.one': '{count} opcja', 'admin.addons.subOptions.few': '{count} opcje', diff --git a/shared/src/i18n/ru/admin.ts b/shared/src/i18n/ru/admin.ts index 98ad8c42d..16c41cbcb 100644 --- a/shared/src/i18n/ru/admin.ts +++ b/shared/src/i18n/ru/admin.ts @@ -685,6 +685,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Дополнение обновлено', 'admin.addons.toast.error': 'Не удалось обновить дополнение', 'admin.addons.group.count': 'Включено {enabled} из {total}', + 'admin.addons.search': 'Поиск дополнений', + 'admin.addons.filter': 'Показать', + 'admin.addons.filter.all': 'Все', + 'admin.addons.filter.on': 'Включённые', + 'admin.addons.filter.off': 'Выключенные', + 'admin.addons.noMatch': 'Ни одно дополнение не подходит под поиск или фильтр.', 'admin.addons.subOptions': '{count} параметра', 'admin.addons.subOptions.one': '{count} параметр', 'admin.addons.subOptions.few': '{count} параметра', diff --git a/shared/src/i18n/sk/admin.ts b/shared/src/i18n/sk/admin.ts index a75627f23..33f9447de 100644 --- a/shared/src/i18n/sk/admin.ts +++ b/shared/src/i18n/sk/admin.ts @@ -734,6 +734,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Doplnok aktualizovaný', 'admin.addons.toast.error': 'Aktualizácia doplnku zlyhala', 'admin.addons.group.count': '{enabled} z {total} zapnuté', + 'admin.addons.search': 'Hľadať doplnky', + 'admin.addons.filter': 'Zobraziť', + 'admin.addons.filter.all': 'Všetky', + 'admin.addons.filter.on': 'Zapnuté', + 'admin.addons.filter.off': 'Vypnuté', + 'admin.addons.noMatch': 'Vyhľadávaniu ani filtru nezodpovedá žiadny doplnok.', 'admin.addons.subOptions': '{count} možností', 'admin.addons.subOptions.one': '{count} možnosť', 'admin.addons.subOptions.few': '{count} možnosti', diff --git a/shared/src/i18n/sv/admin.ts b/shared/src/i18n/sv/admin.ts index 298685448..7dbf319f7 100644 --- a/shared/src/i18n/sv/admin.ts +++ b/shared/src/i18n/sv/admin.ts @@ -732,6 +732,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Tillägget har uppdaterats', 'admin.addons.toast.error': 'Det gick inte att uppdatera tillägget', 'admin.addons.group.count': '{enabled} av {total} aktiverade', + 'admin.addons.search': 'Sök tillägg', + 'admin.addons.filter': 'Visa', + 'admin.addons.filter.all': 'Alla', + 'admin.addons.filter.on': 'På', + 'admin.addons.filter.off': 'Av', + 'admin.addons.noMatch': 'Inget tillägg matchar sökningen eller filtret.', 'admin.addons.subOptions': '{count} alternativ', 'admin.addons.subOptions.one': '{count} alternativ', 'admin.addons.subOptionsOn': '{count} aktiva', diff --git a/shared/src/i18n/th/admin.ts b/shared/src/i18n/th/admin.ts index 21e3e90b1..882ac3280 100644 --- a/shared/src/i18n/th/admin.ts +++ b/shared/src/i18n/th/admin.ts @@ -631,6 +631,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'อัพเดตแอดออนแล้ว', 'admin.addons.toast.error': 'ไม่สามารถอัปเดต addon', 'admin.addons.group.count': 'เปิดใช้งาน {enabled} จาก {total} แล้ว', + 'admin.addons.search': 'ค้นหาส่วนเสริม', + 'admin.addons.filter': 'แสดง', + 'admin.addons.filter.all': 'ทั้งหมด', + 'admin.addons.filter.on': 'เปิด', + 'admin.addons.filter.off': 'ปิด', + 'admin.addons.noMatch': 'ไม่มีส่วนเสริมที่ตรงกับการค้นหาหรือตัวกรอง', 'admin.addons.subOptions': '{count} ตัวเลือก', 'admin.addons.subOptionsOn': 'เปิดอยู่ {count}', 'admin.toursWanderer.title': 'wanderer', diff --git a/shared/src/i18n/tr/admin.ts b/shared/src/i18n/tr/admin.ts index 1c3408202..fb50bccf1 100644 --- a/shared/src/i18n/tr/admin.ts +++ b/shared/src/i18n/tr/admin.ts @@ -720,6 +720,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Eklenti güncellendi', 'admin.addons.toast.error': 'Eklenti güncellenemedi', 'admin.addons.group.count': '{total} eklentiden {enabled} tanesi etkin', + 'admin.addons.search': 'Eklentilerde ara', + 'admin.addons.filter': 'Göster', + 'admin.addons.filter.all': 'Tümü', + 'admin.addons.filter.on': 'Açık', + 'admin.addons.filter.off': 'Kapalı', + 'admin.addons.noMatch': 'Aramaya veya filtreye uyan eklenti yok.', 'admin.addons.subOptions': '{count} seçenek', 'admin.addons.subOptions.one': '{count} seçenek', 'admin.addons.subOptionsOn': '{count} açık', diff --git a/shared/src/i18n/uk/admin.ts b/shared/src/i18n/uk/admin.ts index 2faef0902..eaacace7e 100644 --- a/shared/src/i18n/uk/admin.ts +++ b/shared/src/i18n/uk/admin.ts @@ -682,6 +682,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Доповнення оновлено', 'admin.addons.toast.error': 'Не вдалося оновити доповнення', 'admin.addons.group.count': 'Увімкнено {enabled} з {total}', + 'admin.addons.search': 'Пошук доповнень', + 'admin.addons.filter': 'Показати', + 'admin.addons.filter.all': 'Усі', + 'admin.addons.filter.on': 'Увімкнені', + 'admin.addons.filter.off': 'Вимкнені', + 'admin.addons.noMatch': 'Жодне доповнення не відповідає пошуку чи фільтру.', 'admin.addons.subOptions': '{count} параметра', 'admin.addons.subOptions.one': '{count} параметр', 'admin.addons.subOptions.few': '{count} параметри', diff --git a/shared/src/i18n/vi/admin.ts b/shared/src/i18n/vi/admin.ts index d142940c1..87384f2a3 100644 --- a/shared/src/i18n/vi/admin.ts +++ b/shared/src/i18n/vi/admin.ts @@ -724,6 +724,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': 'Đã cập nhật tiện ích bổ sung', 'admin.addons.toast.error': 'Không thể cập nhật tiện ích bổ sung', 'admin.addons.group.count': '{enabled} trên {total} đã bật', + 'admin.addons.search': 'Tìm tiện ích', + 'admin.addons.filter': 'Hiển thị', + 'admin.addons.filter.all': 'Tất cả', + 'admin.addons.filter.on': 'Bật', + 'admin.addons.filter.off': 'Tắt', + 'admin.addons.noMatch': 'Không có tiện ích nào khớp với tìm kiếm hoặc bộ lọc.', 'admin.addons.subOptions': '{count} tùy chọn', 'admin.addons.subOptionsOn': '{count} đang bật', 'admin.toursWanderer.title': 'wanderer', diff --git a/shared/src/i18n/zh-TW/admin.ts b/shared/src/i18n/zh-TW/admin.ts index 9080b8a27..cfaf1290b 100644 --- a/shared/src/i18n/zh-TW/admin.ts +++ b/shared/src/i18n/zh-TW/admin.ts @@ -645,6 +645,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': '擴充套件已更新', 'admin.addons.toast.error': '更新擴充套件失敗', 'admin.addons.group.count': '{total} 項中已啟用 {enabled} 項', + 'admin.addons.search': '搜尋外掛', + 'admin.addons.filter': '顯示', + 'admin.addons.filter.all': '全部', + 'admin.addons.filter.on': '已開啟', + 'admin.addons.filter.off': '已關閉', + 'admin.addons.noMatch': '沒有外掛符合搜尋或篩選條件。', 'admin.addons.subOptions': '{count} 個選項', 'admin.addons.subOptionsOn': '已開啟 {count} 個', 'admin.toursWanderer.title': 'wanderer', diff --git a/shared/src/i18n/zh/admin.ts b/shared/src/i18n/zh/admin.ts index 51c205137..4193e46e8 100644 --- a/shared/src/i18n/zh/admin.ts +++ b/shared/src/i18n/zh/admin.ts @@ -596,6 +596,12 @@ const admin: TranslationStrings = { 'admin.addons.toast.updated': '扩展已更新', 'admin.addons.toast.error': '更新扩展失败', 'admin.addons.group.count': '{total} 项中已启用 {enabled} 项', + 'admin.addons.search': '搜索插件', + 'admin.addons.filter': '显示', + 'admin.addons.filter.all': '全部', + 'admin.addons.filter.on': '已开启', + 'admin.addons.filter.off': '已关闭', + 'admin.addons.noMatch': '没有插件符合搜索或筛选条件。', 'admin.addons.subOptions': '{count} 个选项', 'admin.addons.subOptionsOn': '已开启 {count} 个', 'admin.toursWanderer.title': 'wanderer',