feat(sidebar): manage-collective link as last nav entry with spacer

The DesktopSidebar previously had no entry point for /collective/manage,
making member-management and the new common-items / tags subsections
unreachable from the chrome on desktop. The MobileDrawer already linked
it from inside the collective switcher block; desktop didn't.

- Adds a `<div aria-hidden>` divider with `my-2 border-t` to separate
  the section nav (lists / tasks / notes / search) from the manage
  entry.
- Adds a `Users`-iconed link to `/collective/manage` as the last nav
  item, with the same active-state styling as the other entries.
- Not gated by `$enabledSections` (membership management is orthogonal
  to feature toggles — same call as the Admin entry in the footer).
- Reuses the existing `m.manage_title()` Paraglide string; no new
  strings.

Tests: tests/e2e/sidebar-manage-link.test.ts adds SBM-01 (link visible
and navigates) + SBM-02 (divider present in <nav>).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-18 14:47:03 +02:00
parent 40a59ef581
commit c0e5b5ed7f
2 changed files with 54 additions and 1 deletions

View File

@@ -9,7 +9,7 @@
import CreateCollectiveModal from '$lib/components/CreateCollectiveModal.svelte';
import OfflineChip from '$lib/components/OfflineChip.svelte';
import * as m from '$lib/paraglide/messages';
import { ShoppingCart, CheckSquare, FileText, Search, Settings, Plus, Shield } from 'lucide-svelte';
import { ShoppingCart, CheckSquare, FileText, Search, Settings, Plus, Shield, Users } from 'lucide-svelte';
// Fase 12.3.1: nav items declared with their section key so we can filter
// by $enabledSections (precedence resolved client-side; see features.ts).
@@ -99,6 +99,19 @@
{label()}
</a>
{/each}
<!-- Spacer + manage-collective entry, last in nav. Always visible
(no section-flag gating) since membership management is orthogonal
to feature toggles. -->
<div class="my-2 border-t border-black/10 dark:border-white/10" aria-hidden="true"></div>
<a
href="/collective/manage"
data-testid="sidebar-manage-link"
class="mb-0.5 flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium transition-colors {activePath.startsWith('/collective/manage') ? 'bg-black/10 text-slate-900 dark:bg-white/10 dark:text-slate-50' : 'text-slate-500 hover:bg-black/5 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-white/5 dark:hover:text-slate-50'}"
>
<Users size={16} strokeWidth={1.5} />
{m.manage_title()}
</a>
</nav>
<div class="p-3 pt-2">

View File

@@ -0,0 +1,40 @@
/**
* SBM-series — desktop sidebar manage-collective entry
*
* The DesktopSidebar gained a last-position link to /collective/manage
* after the nav loop, preceded by a visual spacer (divider). The mobile
* drawer already had this link; desktop did not, leaving the page
* unreachable from the chrome.
*/
import { test, expect } from '@playwright/test';
import { USERS } from '../fixtures/users.js';
import { loginAs } from '../fixtures/login.js';
const DESKTOP = { width: 1280, height: 800 };
test.describe('Desktop sidebar — manage collective link', () => {
test('SBM-01: the manage link is the last nav entry and navigates to /collective/manage', async ({ page }) => {
await page.setViewportSize(DESKTOP);
await loginAs(page, USERS.ana);
await page.goto('/lists');
const link = page.getByTestId('sidebar-manage-link');
await expect(link).toBeVisible({ timeout: 10_000 });
await link.click();
await expect(page).toHaveURL(/\/collective\/manage/);
});
test('SBM-02: a horizontal divider separates the manage link from the section nav', async ({ page }) => {
await page.setViewportSize(DESKTOP);
await loginAs(page, USERS.ana);
await page.goto('/lists');
// The divider sits inside the same <nav>, immediately before the manage link.
const manageLink = page.getByTestId('sidebar-manage-link');
await expect(manageLink).toBeVisible({ timeout: 10_000 });
const dividerCount = await page.locator('nav > div[aria-hidden="true"]').count();
expect(dividerCount).toBeGreaterThanOrEqual(1);
});
});