From 272df55cd4c7f4198ab93121a9faabee6b86626d Mon Sep 17 00:00:00 2001 From: Maurice Date: Tue, 29 Sep 2026 20:25:25 +0200 Subject: [PATCH] feat(packing): sort the packing list A to Z A switch at the end of the list's tab row shows every list's items in name order, so checking whether something is already on the list no longer means reading all of it. The order follows the reader's language (accents beside their letter, "Shirt 2" before "Shirt 10") and the empty list stand-in stays at the bottom. Sorting only changes what is shown: the manual order stays stored and comes back with a second click. While sorted the drag grips step aside, the box keeps its column, and the choice is remembered per browser. --- .../Packing/PackingListPanel.test.tsx | 38 +++++++++++++++++++ .../Packing/PackingListPanelCategoryGroup.tsx | 7 +++- .../Packing/PackingListPanelItemRow.tsx | 2 + .../Packing/PackingListPanelList.tsx | 3 +- .../Packing/PackingListPanelViewTabs.tsx | 20 +++++++--- .../Packing/packingListPanel.helpers.test.ts | 24 +++++++++++- .../Packing/packingListPanel.helpers.ts | 18 ++++++++- .../components/Packing/usePackingListPanel.ts | 24 ++++++++++-- shared/src/i18n/ar/packing.ts | 2 + shared/src/i18n/az/packing.ts | 2 + shared/src/i18n/br/packing.ts | 2 + shared/src/i18n/ca/packing.ts | 2 + shared/src/i18n/cs/packing.ts | 2 + shared/src/i18n/de/packing.ts | 2 + shared/src/i18n/en/packing.ts | 2 + shared/src/i18n/es/packing.ts | 2 + shared/src/i18n/et/packing.ts | 2 + shared/src/i18n/fr/packing.ts | 2 + shared/src/i18n/gr/packing.ts | 2 + shared/src/i18n/hu/packing.ts | 2 + shared/src/i18n/id/packing.ts | 2 + shared/src/i18n/it/packing.ts | 2 + shared/src/i18n/ja/packing.ts | 2 + shared/src/i18n/ko/packing.ts | 2 + shared/src/i18n/nl/packing.ts | 2 + shared/src/i18n/pl/packing.ts | 2 + shared/src/i18n/ru/packing.ts | 2 + shared/src/i18n/sk/packing.ts | 2 + shared/src/i18n/sv/packing.ts | 2 + shared/src/i18n/th/packing.ts | 2 + shared/src/i18n/tr/packing.ts | 2 + shared/src/i18n/uk/packing.ts | 2 + shared/src/i18n/vi/packing.ts | 2 + shared/src/i18n/zh-TW/packing.ts | 2 + shared/src/i18n/zh/packing.ts | 2 + 35 files changed, 176 insertions(+), 14 deletions(-) diff --git a/client/src/components/Packing/PackingListPanel.test.tsx b/client/src/components/Packing/PackingListPanel.test.tsx index 883a1e8e3..9918b05a0 100644 --- a/client/src/components/Packing/PackingListPanel.test.tsx +++ b/client/src/components/Packing/PackingListPanel.test.tsx @@ -236,6 +236,44 @@ describe('PackingListPanel', () => { expect(screen.getByText('No items match this filter')).toBeInTheDocument(); }); + it('FE-COMP-PACKING-093: the A to Z switch sorts each list by name and a second click restores the manual order', async () => { + const user = userEvent.setup(); + const items = [ + buildPackingItem({ id: 1, name: 'Toothbrush', category: 'Bag' }), + buildPackingItem({ id: 2, name: 'adapter', category: 'Bag' }), + buildPackingItem({ id: 3, name: 'Shirt', category: 'Bag' }), + ]; + render(); + const order = () => ['Toothbrush', 'adapter', 'Shirt'] + .map(n => [n, screen.getByText(n)] as const) + .sort(([, a], [, b]) => (a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1)) + .map(([n]) => n); + const toggle = screen.getByRole('button', { name: 'A to Z' }); + + expect(order()).toEqual(['Toothbrush', 'adapter', 'Shirt']); + expect(toggle).toHaveAttribute('aria-pressed', 'false'); + + await user.click(toggle); + expect(order()).toEqual(['adapter', 'Shirt', 'Toothbrush']); + expect(toggle).toHaveAttribute('aria-pressed', 'true'); + expect(localStorage.getItem('trek:packing-sort')).toBe('name'); + + await user.click(toggle); + expect(order()).toEqual(['Toothbrush', 'adapter', 'Shirt']); + expect(localStorage.getItem('trek:packing-sort')).toBe('manual'); + }); + + it('FE-COMP-PACKING-094: sorted A to Z the rows cannot be dragged, and the choice is remembered', () => { + localStorage.setItem('trek:packing-sort', 'name'); + const items = [ + buildPackingItem({ id: 1, name: 'Toothbrush', category: 'Bag' }), + buildPackingItem({ id: 2, name: 'adapter', category: 'Bag' }), + ]; + const { container } = render(); + expect(screen.getByRole('button', { name: 'A to Z' })).toHaveAttribute('aria-pressed', 'true'); + expect(container.querySelector('[draggable="true"]')).toBeNull(); + }); + it('FE-COMP-PACKING-023: inline edit item name via pencil icon calls PUT', async () => { const user = userEvent.setup(); const item = buildPackingItem({ id: 42, name: 'Sunscreen', category: 'Toiletries' }); diff --git a/client/src/components/Packing/PackingListPanelCategoryGroup.tsx b/client/src/components/Packing/PackingListPanelCategoryGroup.tsx index 3654fc0c2..ac2521749 100644 --- a/client/src/components/Packing/PackingListPanelCategoryGroup.tsx +++ b/client/src/components/Packing/PackingListPanelCategoryGroup.tsx @@ -35,6 +35,9 @@ interface KategorieGruppeProps { // order is global, so a within-category drag is mapped back onto the full list. allItems: PackingItem[] onReorder: (orderedIds: number[]) => void + // Off while the list is shown A-Z: a drop there would move an item in an order + // the reader cannot see. + reorderable?: boolean // Three-tier sharing (#858) — threaded down to each item's share control. currentUserId?: number onSetSharing?: (id: number, visibility: 'common' | 'personal' | 'shared', recipientIds: number[]) => void @@ -43,7 +46,7 @@ interface KategorieGruppeProps { onLeave?: (id: number, userId: number) => void } -export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRename, onDeleteAll, onDeleteItem, onAddItem, assignees, tripMembers, onSetAssignees, bagTrackingEnabled, bags, onCreateBag, canEdit = true, allItems, onReorder, currentUserId, onSetSharing, onClone, onJoin, onLeave }: KategorieGruppeProps) { +export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRename, onDeleteAll, onDeleteItem, onAddItem, assignees, tripMembers, onSetAssignees, bagTrackingEnabled, bags, onCreateBag, canEdit = true, allItems, onReorder, reorderable = true, currentUserId, onSetSharing, onClone, onJoin, onLeave }: KategorieGruppeProps) { const [offen, setOffen] = useState(true) const [dragId, setDragId] = useState(null) const [overId, setOverId] = useState(null) @@ -280,7 +283,7 @@ export function KategorieGruppe({ kategorie, items, tripId, allCategories, onRen {}} onDelete={onDeleteItem} bagTrackingEnabled={bagTrackingEnabled} bags={bags} onCreateBag={onCreateBag} canEdit={canEdit} divider={index > 0} tripMembers={tripMembers} currentUserId={currentUserId} onSetSharing={onSetSharing} onClone={onClone} onJoin={onJoin} onLeave={onLeave} - drag={canEdit ? { + drag={canEdit && reorderable ? { isDragging: dragId === item.id, isOver: overId === item.id && dragId !== null && dragId !== item.id, onStart: (id) => { setDragId(id); setOverId(null) }, diff --git a/client/src/components/Packing/PackingListPanelItemRow.tsx b/client/src/components/Packing/PackingListPanelItemRow.tsx index 0af802eff..b1ca760f2 100644 --- a/client/src/components/Packing/PackingListPanelItemRow.tsx +++ b/client/src/components/Packing/PackingListPanelItemRow.tsx @@ -161,6 +161,8 @@ export function ArtikelZeile({ item, tripId, categories, onCategoryChange: _onCa )} + {/* Sorted A-Z there is nothing to drag, but the box keeps its column. */} + {!canDrag && canEdit && !isPlaceholder && } {/* The phone's box: filled with the accent once packed, an outline until then. */} + + )} ) } diff --git a/client/src/components/Packing/packingListPanel.helpers.test.ts b/client/src/components/Packing/packingListPanel.helpers.test.ts index 87ccf071f..30f09d5bc 100644 --- a/client/src/components/Packing/packingListPanel.helpers.test.ts +++ b/client/src/components/Packing/packingListPanel.helpers.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { katColor, itemWeight, bagFillPct, bagTotalWeight, countsTowardsMyLoad, isMarkdownList, parseCsvLine, parseImportLines, unassignedTotalWeight } from './packingListPanel.helpers' +import { katColor, itemWeight, bagFillPct, bagTotalWeight, countsTowardsMyLoad, isMarkdownList, parseCsvLine, parseImportLines, sortItemsByName, unassignedTotalWeight } from './packingListPanel.helpers' import { KAT_COLORS } from './packingListPanel.constants' describe('packingListPanel.helpers', () => { @@ -219,4 +219,26 @@ describe('packingListPanel.helpers', () => { expect(unassignedTotalWeight(undefined, [])).toBe(0) }) }) + describe('sortItemsByName', () => { + const names = (items: { name: string }[]) => items.map(i => i.name) + + it('orders by name, ignoring case and accents the way the language does', () => { + const items = [{ name: 'shirt' }, { name: 'Éponge' }, { name: 'adapter' }, { name: 'Zahnbürste' }, { name: 'Bag' }] + expect(names(sortItemsByName(items, 'en'))).toEqual(['adapter', 'Bag', 'Éponge', 'shirt', 'Zahnbürste']) + }) + + it('reads numbers as numbers', () => { + expect(names(sortItemsByName([{ name: 'Shirt 10' }, { name: 'Shirt 2' }], 'en'))).toEqual(['Shirt 2', 'Shirt 10']) + }) + + it('keeps the placeholder of an empty list at the bottom', () => { + expect(names(sortItemsByName([{ name: '...' }, { name: 'Zip bag' }, { name: 'Adapter' }], 'en'))).toEqual(['Adapter', 'Zip bag', '...']) + }) + + it('leaves the array it was given in its manual order', () => { + const items = [{ name: 'b' }, { name: 'a' }] + sortItemsByName(items, 'en') + expect(names(items)).toEqual(['b', 'a']) + }) + }) }) diff --git a/client/src/components/Packing/packingListPanel.helpers.ts b/client/src/components/Packing/packingListPanel.helpers.ts index aafa9b501..09884335f 100644 --- a/client/src/components/Packing/packingListPanel.helpers.ts +++ b/client/src/components/Packing/packingListPanel.helpers.ts @@ -1,4 +1,4 @@ -import { KAT_COLORS } from './packingListPanel.constants' +import { KAT_COLORS, PACKING_PLACEHOLDER_NAME } from './packingListPanel.constants' // Stable color assignment: category name → index via simple hash export function katColor(kat: string, allCategories?: string[]): string { @@ -180,3 +180,19 @@ export const parseImportLines = (text: string): ParsedImportItem[] => { return { ...splitQuantity(parts[0]), category: undefined, weight_grams: undefined, bag: undefined, checked: false } }).filter(i => i.name) } + +/** + * One list's items in name order, for the A-Z view. Compared the way the reader's + * language sorts (accents next to their letter, "Shirt 2" before "Shirt 10"), and + * the '...' stand-in of an empty list stays at the bottom where it always sits. + * Returns a new array; the stored manual order is left alone. + */ +export function sortItemsByName(items: T[], locale?: string): T[] { + const collator = new Intl.Collator(locale, { sensitivity: 'base', numeric: true }) + return [...items].sort((a, b) => { + const pa = a.name === PACKING_PLACEHOLDER_NAME + const pb = b.name === PACKING_PLACEHOLDER_NAME + if (pa !== pb) return pa ? 1 : -1 + return collator.compare(a.name.trim(), b.name.trim()) + }) +} diff --git a/client/src/components/Packing/usePackingListPanel.ts b/client/src/components/Packing/usePackingListPanel.ts index 0d57ed692..a3efe4509 100644 --- a/client/src/components/Packing/usePackingListPanel.ts +++ b/client/src/components/Packing/usePackingListPanel.ts @@ -11,7 +11,11 @@ import { useNetworkMode } from '../../hooks/useNetworkMode' import { useBagTotalsPing } from './useBagTotalsPing' import type { PackingItem, PackingBag } from '../../types' import { BAG_COLORS, PACKING_PLACEHOLDER_NAME } from './packingListPanel.constants' -import { parseImportLines } from './packingListPanel.helpers' +import { parseImportLines, sortItemsByName } from './packingListPanel.helpers' + +const PACKING_SORT_KEY = 'trek:packing-sort' + +export type PackingSort = 'manual' | 'name' export interface TripMember { id: number @@ -50,6 +54,15 @@ export interface PackingListPanelProps { */ export function usePackingList({ tripId, items, openImportSignal = 0, addCategorySignal = 0, saveTemplateSignal = 0, inlineHeader = true, view: viewProp, onViewChange }: PackingListPanelProps) { const [filter, setFilter] = useState('alle') // 'alle' | 'offen' | 'erledigt' + // A-Z only changes what is shown; the manual order stays stored underneath and + // comes back when the switch is turned off. Remembered per browser. + const [sort, setSortState] = useState(() => { + try { return localStorage.getItem(PACKING_SORT_KEY) === 'name' ? 'name' : 'manual' } catch { return 'manual' } + }) + const setSort = (next: PackingSort) => { + setSortState(next) + try { localStorage.setItem(PACKING_SORT_KEY, next) } catch { /* storage unavailable: the choice lasts until reload */ } + } // Three-tier sharing (#858): 'common' = the group pool (where existing items // live — non-breaking), 'personal' = my own list (private + shared-to-me). const [ownView, setOwnView] = useState<'common' | 'personal'>('common') @@ -65,7 +78,7 @@ export function usePackingList({ tripId, items, openImportSignal = 0, addCategor const isAdmin = useAuthStore((s) => s.user?.role === 'admin') const currentUserId = useAuthStore((s) => s.user?.id) const toast = useToast() - const { t } = useTranslation() + const { t, locale } = useTranslation() // Trip members & category assignees const [tripMembers, setTripMembers] = useState([]) @@ -120,8 +133,11 @@ export function usePackingList({ tripId, items, openImportSignal = 0, addCategor if (!groups[kat]) groups[kat] = [] groups[kat].push(item) } + if (sort === 'name') { + for (const kat of Object.keys(groups)) groups[kat] = sortItemsByName(groups[kat], locale) + } return groups - }, [viewItems, filter, t]) + }, [viewItems, filter, sort, locale, t]) const abgehakt = viewItems.filter(i => i.checked).length const fortschritt = viewItems.length > 0 ? Math.round((abgehakt / viewItems.length) * 100) : 0 @@ -399,7 +415,7 @@ export function usePackingList({ tripId, items, openImportSignal = 0, addCategor view, setView, currentUserId, handleSetSharing, handleCloneItem, handleJoinItem, handleLeaveItem, tripId, items, inlineHeader, t, canEdit, isAdmin, font, reorderPackingItems, - filter, setFilter, addingCategory, setAddingCategory, newCatName, setNewCatName, + filter, setFilter, sort, setSort, addingCategory, setAddingCategory, newCatName, setNewCatName, tripMembers, categoryAssignees, handleSetAssignees, allCategories, gruppiert, abgehakt, fortschritt, handleAddItemToCategory, handleAddNewCategory, handleRenameCategory, handleDeleteCategory, handleDeleteItem, handleClearChecked, bagTrackingEnabled, bags, unassignedWeightGrams, serverWeightsFresh, newBagName, setNewBagName, showAddBag, setShowAddBag, showBagModal, setShowBagModal, diff --git a/shared/src/i18n/ar/packing.ts b/shared/src/i18n/ar/packing.ts index b4ab3dd77..39d310387 100644 --- a/shared/src/i18n/ar/packing.ts +++ b/shared/src/i18n/ar/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'الكل', 'packing.filterOpen': 'مفتوح', 'packing.filterDone': 'تم', + 'packing.sortBy': 'ترتيب حسب', + 'packing.sortByName': 'أ إلى ي', 'packing.emptyTitle': 'قائمة التجهيز فارغة', 'packing.emptyFiltered': 'لا توجد عناصر مطابقة لهذا الفلتر', 'packing.menuRename': 'إعادة تسمية', diff --git a/shared/src/i18n/az/packing.ts b/shared/src/i18n/az/packing.ts index f79be650b..bda9dfcb3 100644 --- a/shared/src/i18n/az/packing.ts +++ b/shared/src/i18n/az/packing.ts @@ -46,6 +46,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Hamısı', 'packing.filterOpen': 'Hazır deyil', 'packing.filterDone': 'Hazırdır', + 'packing.sortBy': 'Sıralama', + 'packing.sortByName': 'A-dan Z-yə', 'packing.emptyTitle': 'Baqaj siyahısı boşdur', 'packing.emptyFiltered': diff --git a/shared/src/i18n/br/packing.ts b/shared/src/i18n/br/packing.ts index c9ae4a9ae..e3eae12a2 100644 --- a/shared/src/i18n/br/packing.ts +++ b/shared/src/i18n/br/packing.ts @@ -42,6 +42,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Todos', 'packing.filterOpen': 'Abertos', 'packing.filterDone': 'Prontos', + 'packing.sortBy': 'Ordenar por', + 'packing.sortByName': 'A a Z', 'packing.emptyTitle': 'A lista de mala está vazia', 'packing.emptyFiltered': 'Nenhum item corresponde ao filtro', 'packing.menuRename': 'Renomear', diff --git a/shared/src/i18n/ca/packing.ts b/shared/src/i18n/ca/packing.ts index 1786e4823..4ec8def6b 100644 --- a/shared/src/i18n/ca/packing.ts +++ b/shared/src/i18n/ca/packing.ts @@ -31,6 +31,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Tot', 'packing.filterOpen': 'Pendents', 'packing.filterDone': 'Fet', + 'packing.sortBy': 'Ordena per', + 'packing.sortByName': 'A a Z', 'packing.emptyTitle': "La llista d'equipatge és buida", 'packing.emptyFiltered': 'Cap element coincideix amb aquest filtre', 'packing.menuRename': 'Canvia el nom', diff --git a/shared/src/i18n/cs/packing.ts b/shared/src/i18n/cs/packing.ts index 89806c108..300688422 100644 --- a/shared/src/i18n/cs/packing.ts +++ b/shared/src/i18n/cs/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Vše', 'packing.filterOpen': 'K zabalení', 'packing.filterDone': 'Hotovo', + 'packing.sortBy': 'Seřadit podle', + 'packing.sortByName': 'A až Z', 'packing.emptyTitle': 'Seznam věcí je prázdný', 'packing.emptyFiltered': 'Žádné položky neodpovídají filtru', 'packing.menuRename': 'Přejmenovat', diff --git a/shared/src/i18n/de/packing.ts b/shared/src/i18n/de/packing.ts index c5fca57c6..9dece6d25 100644 --- a/shared/src/i18n/de/packing.ts +++ b/shared/src/i18n/de/packing.ts @@ -41,6 +41,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Alle', 'packing.filterOpen': 'Offen', 'packing.filterDone': 'Erledigt', + 'packing.sortBy': 'Sortieren nach', + 'packing.sortByName': 'A bis Z', 'packing.emptyTitle': 'Packliste ist leer', 'packing.emptyFiltered': 'Keine Gegenstände in diesem Filter', 'packing.menuRename': 'Umbenennen', diff --git a/shared/src/i18n/en/packing.ts b/shared/src/i18n/en/packing.ts index 8034a98ef..b8cf91929 100644 --- a/shared/src/i18n/en/packing.ts +++ b/shared/src/i18n/en/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'All', 'packing.filterOpen': 'Open', 'packing.filterDone': 'Done', + 'packing.sortBy': 'Sort by', + 'packing.sortByName': 'A to Z', 'packing.emptyTitle': 'Packing list is empty', 'packing.emptyFiltered': 'No items match this filter', 'packing.menuRename': 'Rename', diff --git a/shared/src/i18n/es/packing.ts b/shared/src/i18n/es/packing.ts index 7c840e91c..233ecd2e6 100644 --- a/shared/src/i18n/es/packing.ts +++ b/shared/src/i18n/es/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Todo', 'packing.filterOpen': 'Pendientes', 'packing.filterDone': 'Hecho', + 'packing.sortBy': 'Ordenar por', + 'packing.sortByName': 'A a Z', 'packing.emptyTitle': 'La lista de equipaje está vacía', 'packing.emptyFiltered': 'Ningún elemento coincide con este filtro', 'packing.menuRename': 'Renombrar', diff --git a/shared/src/i18n/et/packing.ts b/shared/src/i18n/et/packing.ts index 51ff2518a..06bda1955 100644 --- a/shared/src/i18n/et/packing.ts +++ b/shared/src/i18n/et/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Kõik', 'packing.filterOpen': 'Pakkimata', 'packing.filterDone': 'Pakitud', + 'packing.sortBy': 'Sordi', + 'packing.sortByName': 'A kuni Ü', 'packing.emptyTitle': 'Pakkimisnimekiri on tühi', 'packing.emptyFiltered': 'Ükski ese ei vasta sellele filtrile', 'packing.menuRename': 'Nimeta ümber', diff --git a/shared/src/i18n/fr/packing.ts b/shared/src/i18n/fr/packing.ts index acc75931c..c1e6d6ddb 100644 --- a/shared/src/i18n/fr/packing.ts +++ b/shared/src/i18n/fr/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Tous', 'packing.filterOpen': 'À faire', 'packing.filterDone': 'Fait', + 'packing.sortBy': 'Trier par', + 'packing.sortByName': 'A à Z', 'packing.emptyTitle': 'La liste de bagages est vide', 'packing.emptyFiltered': 'Aucun article ne correspond à ce filtre', 'packing.menuRename': 'Renommer', diff --git a/shared/src/i18n/gr/packing.ts b/shared/src/i18n/gr/packing.ts index ad21823da..b489e37cd 100644 --- a/shared/src/i18n/gr/packing.ts +++ b/shared/src/i18n/gr/packing.ts @@ -41,6 +41,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Όλα', 'packing.filterOpen': 'Ανοιχτά', 'packing.filterDone': 'Ολοκληρωμένα', + 'packing.sortBy': 'Ταξινόμηση κατά', + 'packing.sortByName': 'Α έως Ω', 'packing.emptyTitle': 'Η λίστα αποσκευών είναι κενή', 'packing.emptyFiltered': 'Κανένα αντικείμενο δεν ταιριάζει με αυτό το φίλτρο', 'packing.menuRename': 'Μετονομασία', diff --git a/shared/src/i18n/hu/packing.ts b/shared/src/i18n/hu/packing.ts index 8e0905b0a..19f539c6c 100644 --- a/shared/src/i18n/hu/packing.ts +++ b/shared/src/i18n/hu/packing.ts @@ -41,6 +41,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Összes', 'packing.filterOpen': 'Nyitott', 'packing.filterDone': 'Kész', + 'packing.sortBy': 'Rendezés', + 'packing.sortByName': 'A-tól Z-ig', 'packing.emptyTitle': 'A csomagolási lista üres', 'packing.emptyFiltered': 'Nincs elem ebben a szűrőben', 'packing.menuRename': 'Átnevezés', diff --git a/shared/src/i18n/id/packing.ts b/shared/src/i18n/id/packing.ts index 8e274e2e4..ee2398772 100644 --- a/shared/src/i18n/id/packing.ts +++ b/shared/src/i18n/id/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Semua', 'packing.filterOpen': 'Belum', 'packing.filterDone': 'Selesai', + 'packing.sortBy': 'Urutkan', + 'packing.sortByName': 'A ke Z', 'packing.emptyTitle': 'Daftar bawaan kosong', 'packing.emptyFiltered': 'Tidak ada item yang cocok dengan filter ini', 'packing.menuRename': 'Ganti Nama', diff --git a/shared/src/i18n/it/packing.ts b/shared/src/i18n/it/packing.ts index d46faad3a..c519df77c 100644 --- a/shared/src/i18n/it/packing.ts +++ b/shared/src/i18n/it/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Tutti', 'packing.filterOpen': 'Da fare', 'packing.filterDone': 'Fatto', + 'packing.sortBy': 'Ordina per', + 'packing.sortByName': 'A-Z', 'packing.emptyTitle': 'La lista valigia è vuota', 'packing.emptyFiltered': 'Nessun elemento corrisponde a questo filtro', 'packing.menuRename': 'Rinomina', diff --git a/shared/src/i18n/ja/packing.ts b/shared/src/i18n/ja/packing.ts index 86dfb38e6..a83df28de 100644 --- a/shared/src/i18n/ja/packing.ts +++ b/shared/src/i18n/ja/packing.ts @@ -39,6 +39,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'すべて', 'packing.filterOpen': '未完了', 'packing.filterDone': '完了', + 'packing.sortBy': '並べ替え', + 'packing.sortByName': '名前順', 'packing.emptyTitle': '持ち物リストは空です', 'packing.emptyFiltered': 'このフィルターに一致する項目はありません', 'packing.menuRename': '名前を変更', diff --git a/shared/src/i18n/ko/packing.ts b/shared/src/i18n/ko/packing.ts index 14bcd9d17..ef7097e9d 100644 --- a/shared/src/i18n/ko/packing.ts +++ b/shared/src/i18n/ko/packing.ts @@ -39,6 +39,8 @@ const packing: TranslationStrings = { 'packing.filterAll': '전체', 'packing.filterOpen': '미완료', 'packing.filterDone': '완료', + 'packing.sortBy': '정렬', + 'packing.sortByName': '가나다순', 'packing.emptyTitle': '짐 목록이 비어 있습니다', 'packing.emptyFiltered': '이 필터와 일치하는 항목이 없습니다', 'packing.menuRename': '이름 변경', diff --git a/shared/src/i18n/nl/packing.ts b/shared/src/i18n/nl/packing.ts index e0415a3f8..b4bfd6366 100644 --- a/shared/src/i18n/nl/packing.ts +++ b/shared/src/i18n/nl/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Alle', 'packing.filterOpen': 'Openstaand', 'packing.filterDone': 'Klaar', + 'packing.sortBy': 'Sorteren op', + 'packing.sortByName': 'A tot Z', 'packing.emptyTitle': 'Inpaklijst is leeg', 'packing.emptyFiltered': 'Geen items gevonden voor dit filter', 'packing.menuRename': 'Hernoemen', diff --git a/shared/src/i18n/pl/packing.ts b/shared/src/i18n/pl/packing.ts index ea888f84c..89246f657 100644 --- a/shared/src/i18n/pl/packing.ts +++ b/shared/src/i18n/pl/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Wszystkie', 'packing.filterOpen': 'Do spakowania', 'packing.filterDone': 'Spakowane', + 'packing.sortBy': 'Sortuj według', + 'packing.sortByName': 'Od A do Z', 'packing.emptyTitle': 'Lista pakowania jest pusta', 'packing.emptyFiltered': 'Brak przedmiotów pasujących do filtra', 'packing.menuRename': 'Zmień nazwę', diff --git a/shared/src/i18n/ru/packing.ts b/shared/src/i18n/ru/packing.ts index 65514f7e7..8992a402c 100644 --- a/shared/src/i18n/ru/packing.ts +++ b/shared/src/i18n/ru/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Все', 'packing.filterOpen': 'Не собрано', 'packing.filterDone': 'Собрано', + 'packing.sortBy': 'Сортировка', + 'packing.sortByName': 'От А до Я', 'packing.emptyTitle': 'Список вещей пуст', 'packing.emptyFiltered': 'Нет вещей, соответствующих фильтру', 'packing.menuRename': 'Переименовать', diff --git a/shared/src/i18n/sk/packing.ts b/shared/src/i18n/sk/packing.ts index 6f213362e..a6ed83819 100644 --- a/shared/src/i18n/sk/packing.ts +++ b/shared/src/i18n/sk/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Všetko', 'packing.filterOpen': 'Na zabalenie', 'packing.filterDone': 'Hotovo', + 'packing.sortBy': 'Zoradiť podľa', + 'packing.sortByName': 'A až Z', 'packing.emptyTitle': 'Zoznam vecí je prázdny', 'packing.emptyFiltered': 'Žiadne položky nezodpovedajú tomuto filtru', 'packing.menuRename': 'Premenovať', diff --git a/shared/src/i18n/sv/packing.ts b/shared/src/i18n/sv/packing.ts index ce28034d3..f414ccbab 100644 --- a/shared/src/i18n/sv/packing.ts +++ b/shared/src/i18n/sv/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Alla', 'packing.filterOpen': 'Öppna', 'packing.filterDone': 'Klar', + 'packing.sortBy': 'Sortera efter', + 'packing.sortByName': 'A till Ö', 'packing.emptyTitle': 'Packlistan är tom', 'packing.emptyFiltered': 'Inga föremål matchar detta filter', 'packing.menuRename': 'Döp om', diff --git a/shared/src/i18n/th/packing.ts b/shared/src/i18n/th/packing.ts index 3a7c17b46..559616b50 100644 --- a/shared/src/i18n/th/packing.ts +++ b/shared/src/i18n/th/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': "ทั้งหมด", 'packing.filterOpen': "ยังไม่ได้จัด", 'packing.filterDone': "เสร็จแล้ว", + 'packing.sortBy': 'เรียงตาม', + 'packing.sortByName': 'ก ถึง ฮ', 'packing.emptyTitle': "รายการจัดกระเป๋าว่างเปล่า", 'packing.emptyFiltered': "ไม่มีรายการที่ตรงกับตัวกรองนี้", 'packing.menuRename': "เปลี่ยนชื่อ", diff --git a/shared/src/i18n/tr/packing.ts b/shared/src/i18n/tr/packing.ts index 9cd010cae..baafc52d2 100644 --- a/shared/src/i18n/tr/packing.ts +++ b/shared/src/i18n/tr/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Tüm', 'packing.filterOpen': 'Açık', 'packing.filterDone': 'Tamamlamak', + 'packing.sortBy': 'Sırala', + 'packing.sortByName': "A'dan Z'ye", 'packing.emptyTitle': 'Paket listesi boş', 'packing.emptyFiltered': 'Bu filtreye uyan öğe yok', 'packing.menuRename': 'Yeniden adlandır', diff --git a/shared/src/i18n/uk/packing.ts b/shared/src/i18n/uk/packing.ts index a3ae6ed92..a81e208aa 100644 --- a/shared/src/i18n/uk/packing.ts +++ b/shared/src/i18n/uk/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Усі', 'packing.filterOpen': 'Не зібрано', 'packing.filterDone': 'Зібрано', + 'packing.sortBy': 'Сортування', + 'packing.sortByName': 'Від А до Я', 'packing.emptyTitle': 'Список речей порожній', 'packing.emptyFiltered': 'Немає речей, що відповідають фільтру', 'packing.menuRename': 'Перейменувати', diff --git a/shared/src/i18n/vi/packing.ts b/shared/src/i18n/vi/packing.ts index bae428d27..d3062960f 100644 --- a/shared/src/i18n/vi/packing.ts +++ b/shared/src/i18n/vi/packing.ts @@ -40,6 +40,8 @@ const packing: TranslationStrings = { 'packing.filterAll': 'Tất cả', 'packing.filterOpen': 'Mở', 'packing.filterDone': 'Xong', + 'packing.sortBy': 'Sắp xếp theo', + 'packing.sortByName': 'A đến Z', 'packing.emptyTitle': 'Danh sách đóng gói trống', 'packing.emptyFiltered': 'Không có mục nào phù hợp với bộ lọc này', 'packing.menuRename': 'Đổi tên', diff --git a/shared/src/i18n/zh-TW/packing.ts b/shared/src/i18n/zh-TW/packing.ts index c938e0d34..0ac291f74 100644 --- a/shared/src/i18n/zh-TW/packing.ts +++ b/shared/src/i18n/zh-TW/packing.ts @@ -39,6 +39,8 @@ const packing: TranslationStrings = { 'packing.filterAll': '全部', 'packing.filterOpen': '未完成', 'packing.filterDone': '已完成', + 'packing.sortBy': '排序方式', + 'packing.sortByName': '依名稱', 'packing.emptyTitle': '行李清單為空', 'packing.emptyFiltered': '沒有匹配的物品', 'packing.menuRename': '重新命名', diff --git a/shared/src/i18n/zh/packing.ts b/shared/src/i18n/zh/packing.ts index b2311dfbe..6df5327ba 100644 --- a/shared/src/i18n/zh/packing.ts +++ b/shared/src/i18n/zh/packing.ts @@ -39,6 +39,8 @@ const packing: TranslationStrings = { 'packing.filterAll': '全部', 'packing.filterOpen': '未完成', 'packing.filterDone': '已完成', + 'packing.sortBy': '排序方式', + 'packing.sortByName': '按名称', 'packing.emptyTitle': '行李清单为空', 'packing.emptyFiltered': '没有匹配的物品', 'packing.menuRename': '重命名',