feat: Kategorie "Eigene Module" fuer selbst angelegte Eintraege
CUSTOM_MODULE_CATEGORIES = MODULE_CATEGORIES + custom-modules; API prueft dagegen, das Formular bietet sie an. Die Seitenleiste zeigt die Gruppe wie jede Kategorie nur mit Eintrag und stellt sie immer ans Ende. Nutzerwunsch 29.09.; ohne Migration (category ist Freitext mit IsIn-Pruefung). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { MODULE_CATEGORIES } from '@tessera/shared';
|
||||
import { CUSTOM_MODULE_CATEGORIES } from '@tessera/shared';
|
||||
import {
|
||||
type CustomModule,
|
||||
checkCustomModuleUrl,
|
||||
@@ -142,7 +142,7 @@ export function CustomModuleFormModal({
|
||||
onChange={(e) => setCategory(e.target.value)}
|
||||
className="flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
|
||||
>
|
||||
{MODULE_CATEGORIES.map((c) => (
|
||||
{CUSTOM_MODULE_CATEGORIES.map((c) => (
|
||||
<option key={c} value={c}>
|
||||
{categoryLabel(c)}
|
||||
</option>
|
||||
|
||||
@@ -299,6 +299,32 @@ describe('Sidebar', () => {
|
||||
expect(screen.getByText('Domain-Tools')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Kategorie „Eigene Module“ steht zuletzt und nur, wenn ein Eintrag darin liegt', async () => {
|
||||
mockCustomModules = [
|
||||
customModule({ id: 'cm-1', category: 'custom-modules', name: 'Mein Wiki' }),
|
||||
customModule({ id: 'cm-2', category: 'fleet', name: 'Tankkarte' }),
|
||||
];
|
||||
const Sidebar = await importSidebar();
|
||||
const { unmount } = render(<Sidebar />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Mein Wiki')).toBeInTheDocument();
|
||||
});
|
||||
const groups = Array.from(
|
||||
screen.getByRole('group', { name: 'Kategorien' }).querySelectorAll(':scope > ul > li > button span.truncate'),
|
||||
).map((n) => n.textContent);
|
||||
expect(groups.at(-1)).toBe('custom-modules');
|
||||
expect(groups).toContain('fleet');
|
||||
unmount();
|
||||
|
||||
mockCustomModules = [customModule({ category: 'fleet' })];
|
||||
render(<Sidebar />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('fleet')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.queryByText('custom-modules')).toBeNull();
|
||||
});
|
||||
|
||||
it('eingebaute Module stehen innerhalb einer Kategorie vor eigenen', async () => {
|
||||
mockCustomModules = [customModule({ category: 'Domain-Tools', name: 'Aaa Eigenes' })];
|
||||
const Sidebar = await importSidebar();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { CUSTOM_MODULE_CATEGORY } from '@tessera/shared';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { TesseraLogo } from '@/components/brand/tessera-logo';
|
||||
import { useSidebarStore } from '@/lib/stores/sidebar-store';
|
||||
@@ -192,11 +193,15 @@ export function Sidebar() {
|
||||
categories.get(entry.category)!.push(entry);
|
||||
}
|
||||
|
||||
if (!searchQuery) return Array.from(categories.entries());
|
||||
// „Eigene Module“ steht immer zuletzt, die uebrigen in Fundreihenfolge.
|
||||
const ordered = Array.from(categories.entries()).sort(
|
||||
([a], [b]) => Number(a === CUSTOM_MODULE_CATEGORY) - Number(b === CUSTOM_MODULE_CATEGORY),
|
||||
);
|
||||
if (!searchQuery) return ordered;
|
||||
|
||||
const q = searchQuery.toLowerCase();
|
||||
const result: [string, SidebarEntry[]][] = [];
|
||||
for (const [cat, mods] of categories.entries()) {
|
||||
for (const [cat, mods] of ordered) {
|
||||
if (cat.toLowerCase().includes(q) || categoryLabel(cat).toLowerCase().includes(q)) {
|
||||
result.push([cat, mods]);
|
||||
} else {
|
||||
|
||||
@@ -1396,6 +1396,7 @@
|
||||
"security-tools": "Sicherheit",
|
||||
"fleet": "Fuhrpark",
|
||||
"infrastructure": "Infrastruktur",
|
||||
"procurement": "Beschaffung"
|
||||
"procurement": "Beschaffung",
|
||||
"custom-modules": "Eigene Module"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1396,6 +1396,7 @@
|
||||
"security-tools": "Security",
|
||||
"fleet": "Fleet",
|
||||
"infrastructure": "Infrastructure",
|
||||
"procurement": "Procurement"
|
||||
"procurement": "Procurement",
|
||||
"custom-modules": "Custom modules"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MODULE_CATEGORIES } from '@tessera/shared';
|
||||
import { CUSTOM_MODULE_CATEGORIES } from '@tessera/shared';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import de from './de.json';
|
||||
import en from './en.json';
|
||||
@@ -9,13 +9,13 @@ import en from './en.json';
|
||||
* API) braucht einen Anzeigenamen in `moduleCategories` beider Sprachen —
|
||||
* sonst zeigte die Seitenleiste die rohe Kennung.
|
||||
*/
|
||||
describe('MODULE_CATEGORIES', () => {
|
||||
describe('CUSTOM_MODULE_CATEGORIES (inkl. MODULE_CATEGORIES)', () => {
|
||||
it.each([
|
||||
['de', de],
|
||||
['en', en],
|
||||
] as const)('hat je Kennung einen Schluessel in moduleCategories (%s)', (_lang, messages) => {
|
||||
const labels = messages.moduleCategories as Record<string, string>;
|
||||
for (const category of MODULE_CATEGORIES) {
|
||||
for (const category of CUSTOM_MODULE_CATEGORIES) {
|
||||
expect(labels[category], `moduleCategories.${category}`).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user