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. */}