Next.js Datenanzeige mit API
Erstellt am: 15. Oktober 2024
Beantwortet mit OpenAI 01 Preview von OpenAI01
Erstellt am: 15. Oktober 2024
Beantwortet mit OpenAI 01 Preview von OpenAI01
Übersetzen und verstehen
Ich übersetze die Anforderungen ins Englische, um Next.js 14, API-Calls, Suspense, optimistic UI, Codenstruktur und Datentabelle/Table-Architektur zu verstehen.
Filter the user by ID from JSON data, and if the role is "user," redirect to another page.
Mapping out die Anwendung
Ich skizziere die Nutzung von Next.js 14, die Erstellung von JSON-Daten, die Anzeige in einer TanStack Table, und die Implementierung von Routing-Interzepten für benutzerdefinierte Seiten.
Analyzieren der Dateistruktur
Ich schaue mir die Struktur für eine Next.js 14 App an, auch wenn Next.js 14 noch nicht draußen ist. Welche Ordner wären da sinnvoll?
Überlegen der Architektur
Ich denke darüber nach, wie die Architektur aufgebaut sein könnte: Ein API-Endpunkt liest JSON-Daten, eine Seite zeigt diese in einer Tabelle an, und bei Klick auf eine Zeile öffnet sich ein Sheet-Komponente.
Verstehen der Datenstruktur
Ich denke darüber nach, wie ein JSON-Dateiformat für Benutzerdaten aussehen könnte, inklusive Feldern wie ID, Name, Vorname, E-Mail, Firma und Adresse.
Anzeigen von Benutzerdaten
Nun schaue ich, wie man diese Benutzerdaten in der Anwendung darstellen könnte, indem Daten wie Name, Firma und Adresse angezeigt werden.
Entwickeln und konfigurieren
Zuerst definiere ich API-Routen unter /app/api, implementiere dann einen GET-Handler für das Abrufen von JSON-Daten. Die Hauptseite unter /app/page.js nutzt TanStack Table für die Darstellung und Shadcn/ui für interaktive Tabellenstrukturen.
Einrichten der Filtergruppe
Ich entwickle eine Filtergruppe, um gefilterte Datengruppen für die Sortierung zu identifizieren und eine Sortier-UI aus gefilterten Datengruppen zu planen.
Erkundung von Interzeptierungswegen
Ich beschließe, die Routen in Next.js 13+ zu prüfen. Auf der einen Seite steht die Hauptroute, auf der anderen das Overlay. Ich denke darüber nach, wie die Modalgruppe dazwischenpasst.
Auftragsplanung und Routing
Jetzt erstelle ich die Haupt- und Nutzerdetails-Seiten und definiere parallele Routen für das Modals. Zur besseren Organisation strukturiere ich die Routen mit dem @modals Präfix.
Überprüfe Endpunkte
Interessiert daran, Routen in Next.js richtig einzurichten, stelle ich fest, dass eine geschachtelte Ordnerstruktur möglicherweise komplexer ist. Es könnte sinnvoller sein, einfachere Methoden oder alternative Ansätze zu erkunden.
Der Aufbau der Struktur
Durchsehen und implementieren
Ich schaue mir die API-Rendierroute im Next.js 13+-App-Router an. Mit NextResponse und fs im data/users.json aufrufen und Daten als JSON zurückgeben.
Laden der Daten
In /app/page.js wird der DataTable-Component mit Suspense geladen, um Daten asynchron zu fetchen und anzuzeigen.
Datenbereitstellung
/app/components/DataTable.js nutzt useState und useEffect, um Daten vom Server zu laden und in einer Tabelle darzustellen.
Navigeer een klik
Ik denk na over hoe we de gebruikersinterface en databeheer kunnen optimaliseren, zoals het openen van een model op een rijklik en het tonen van gedetailleerde gebruikersinformatie bij toegang tot /users/[id].
Ordnerstruktur:
/app
/api
/users
route.js
/components
DataTable.js
UserDetailsSheet.js
/users
/[id]
page.js
/(modal)
/users
/[id]
page.js
page.js
layout.js
/data
users.json
Erstelle eine Datei unter /data/users.json
mit folgendem Inhalt:
[ { "id": 1, "name": "Müller", "vorname": "Hans", "email": "[email protected]", "company": "Müller GmbH", "address": "Hauptstraße 1, 12345 Musterstadt" }, { "id": 2, "name": "Schmidt", "vorname": "Lisa", "email": "[email protected]", "company": "Schmidt AG", "address": "Bahnhofstraße 2, 54321 Beispielstadt" } // Füge weitere Nutzer hinzu ]
Erstelle die API-Route unter /app/api/users/route.js
:
import { NextResponse } from 'next/server'; import fs from 'fs'; import path from 'path'; export async function GET(request) { try { const filePath = path.join(process.cwd(), 'data', 'users.json'); const data = fs.readFileSync(filePath, 'utf8'); const users = JSON.parse(data); return NextResponse.json(users); } catch (error) { console.error('Fehler beim Laden der Nutzerdaten:', error); return NextResponse.error(); } }
Erstelle die Hauptseite unter /app/page.js
:
'use client'; import { Suspense } from 'react'; import DataTable from './components/DataTable'; export default function HomePage() { return ( <Suspense fallback={<div>Lade Daten...</div>}> <DataTable /> </Suspense> ); }
Erstelle die DataTable-Komponente unter /app/components/DataTable.js
:
'use client'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { ColumnDef } from '@tanstack/react-table'; import { DataTable } from '@shadcn/ui'; import UserDetailsSheet from './UserDetailsSheet'; export default function DataTableComponent() { const [data, setData] = useState(null); const router = useRouter(); const [selectedUserId, setSelectedUserId] = useState(null); useEffect(() => { fetch('/api/users') .then((res) => res.json()) .then(setData) .catch((error) => { console.error('Fehler beim Laden der Daten:', error); }); }, []); if (!data) { return null; // Suspense kümmert sich um den Ladezustand } const columns = [ { accessorKey: 'id', header: 'ID', }, { accessorKey: 'vorname', header: 'Vorname', }, { accessorKey: 'name', header: 'Name', }, { accessorKey: 'email', header: 'E-Mail', }, ]; const handleRowClick = (row) => { setSelectedUserId(row.original.id); router.push(`/users/${row.original.id}`); }; return ( <> <DataTable columns={columns} data={data} onRowClick={handleRowClick} /> {selectedUserId && ( <UserDetailsSheet userId={selectedUserId} onClose={() => setSelectedUserId(null)} /> )} </> ); }
Erstelle die Sheet-Komponente unter /app/components/UserDetailsSheet.js
:
'use client'; import { useState, useEffect } from 'react'; import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@shadcn/ui'; export default function UserDetailsSheet({ userId, onClose }) { const [user, setUser] = useState(null); useEffect(() => { fetch(`/api/users`) .then((res) => res.json()) .then((users) => { const foundUser = users.find((u) => u.id === userId); setUser(foundUser); }) .catch((error) => { console.error('Fehler beim Laden der Nutzerdetails:', error); }); }, [userId]); if (!user) { return null; } return ( <Sheet isOpen={true} onClose={onClose}> <SheetContent> <SheetHeader> <SheetTitle>{`${user.vorname} ${user.name}`}</SheetTitle> </SheetHeader> <div> <p>E-Mail: {user.email}</p> <p>Firma: {user.company}</p> <p>Adresse: {user.address}</p> </div> </SheetContent> </Sheet> ); }
Erstelle die Seite für die direkte URL unter /app/users/[id]/page.js
:
import { notFound } from 'next/navigation'; import fs from 'fs'; import path from 'path'; export default async function UserDetailPage({ params }) { const { id } = params; const filePath = path.join(process.cwd(), 'data', 'users.json'); const data = fs.readFileSync(filePath, 'utf8'); const users = JSON.parse(data); const user = users.find((u) => u.id === parseInt(id)); if (!user) { notFound(); } return ( <div> <h1>{`${user.vorname} ${user.name}`}</h1> <p>E-Mail: {user.email}</p> <p>Firma: {user.company}</p> <p>Adresse: {user.address}</p> </div> ); }
Erstelle eine parallele Route für das Sheet unter /app/(modal)/users/[id]/page.js
:
'use client'; import { useRouter } from 'next/navigation'; import UserDetailsSheet from '../../../components/UserDetailsSheet'; export default function UserModalPage({ params }) { const { id } = params; const router = useRouter(); const handleClose = () => { router.back(); }; return ( <UserDetailsSheet userId={parseInt(id)} onClose={handleClose} /> ); }
Erstelle ein Layout unter /app/layout.js
:
export default function RootLayout({ children, modal }) { return ( <html> <body> {children} {modal} </body> </html> ); }
/app/page.js
./users/[id]
aufgerufen.(modal)
fängt diese Route ab und zeigt stattdessen das UserDetailsSheet
an./users/[id]
direkt aufgerufen wird, greift die Seite unter /app/users/[id]/page.js
, und die Nutzerdetails werden auf einer eigenen Seite angezeigt.Suspense
, um einen Ladezustand anzuzeigen, während die Daten geladen werden.Stelle sicher, dass alle benötigten Pakete installiert sind:
npm install next react react-dom @tanstack/react-table @shadcn/ui
Mit dieser Struktur und dem Code erfüllst du alle Anforderungen:
Suspense
für den Ladezustand.shadcn/ui
werden für ein konsistentes Design verwendet.Überarbeiten von Code
Ich schaue mir aktuell Typescript React-Code an und erkundige mich dann, wie man ihn modifizieren kann.
Anpassen für Vite.js
Ich denke darüber nach, wie die Navigation in einer Vite.js-Seite integriert werden kann. Extrahiere den Code innerhalb der map
-Methode und erstelle eine separate Funktion davon in der selben Datei.
Anpassung der Dateistruktur
Ich schaue mir die Datei an, um sie für vite.js fit zu machen. Außerdem extrahiere und integriere ich den Code innerhalb der map-Methode in eine neue Funktion im selben Datei.
Funktion erstellen
Ich ziehe in Betracht, den Code im map
-Aufruf in eine separate Funktion namens renderNavLink
zu extrahieren, um die Lesbarkeit zu verbessern.
Überarbeiten und passen
Ich beziehe React ein und passe die Dateitypen an. Der Code zeigt auch, wie Vite.js genutzt wird, um die Navigation in einem Projekt zu integrieren.
Natürlich! Hier ist die angepasste Navigation.tsx
-Datei, die für ein Vite.js-Projekt geeignet ist. Ich habe den Code innerhalb der map
-Methode in eine separate Funktion namens renderNavLink
extrahiert.
// Navigation.tsx import React from 'react'; import { tempUser } from './tempUser'; import { navLinks } from './navLinks'; interface NavLink { url: string; title: string; role: string; } const renderNavLink = (link: NavLink) => { // Überprüfen, ob die Rolle des Links im Rollen-Array des Benutzers vorhanden ist if (tempUser.roles.includes(link.role)) { return ( <li key={link.url}> <a href={link.url}>{link.title}</a> </li> ); } return null; // Link nicht anzeigen, wenn die Rolle nicht übereinstimmt }; const Navigation: React.FC = () => { return ( <nav> <ul> {navLinks.map(renderNavLink)} </ul> </nav> ); }; export default Navigation;
Kompatibilität mit Vite.js:
Extrahieren des Codes innerhalb der map
-Methode:
navLinks.map()
wurde in eine separate Funktion renderNavLink
extrahiert.Typisierung mit TypeScript:
NavLink
-Interface wurde hinzugefügt, um die Typen der Link-Objekte zu definieren.renderNavLink
verwendet dieses Interface für eine genaue Typisierung.Verwendung der renderNavLink
-Funktion:
Navigation
-Komponente wird navLinks.map(renderNavLink)
verwendet, um die Links zu rendern.return
-Anweisung und macht den Code sauberer.Imports:
tempUser
und navLinks
korrekt exportiert werden und in deinem Vite.js-Projekt vorhanden sind.Vite.js-Konfiguration:
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app npm install
npm run dev
Dateistruktur:
Navigation.tsx
-Datei im src
-Verzeichnis deines Vite.js-Projekts.tempUser
und navLinks
entsprechend an, falls sie in unterschiedlichen Verzeichnissen liegen.tempUser
und navLinks
:tempUser.ts
export const tempUser = { roles: ['admin', 'user'], };
navLinks.ts
export const navLinks = [ { url: '/dashboard', title: 'Dashboard', role: 'admin' }, { url: '/profile', title: 'Profile', role: 'user' }, { url: '/settings', title: 'Settings', role: 'admin' }, ];
Navigation
-Komponente:Füge die Navigation
-Komponente in deiner Haupt-App-Komponente ein.
App.tsx
import React from 'react'; import Navigation from './Navigation'; function App() { return ( <div> <Navigation /> {/* Andere Komponenten */} </div> ); } export default App;
Mit diesen Anpassungen ist deine Navigation
-Komponente für ein Vite.js-Projekt geeignet, und der Code innerhalb der map
-Methode wurde in eine separate Funktion extrahiert, wie gewünscht.
Erklärung und Anpassung
Der Benutzer möchte die Datei Navigation.tsx sehen, das Interface von NavLink auf ApplicationLink umbenennen und die Notwendigkeit eines Interfaces erklären.
Passen den Namen an
Ich denke darüber nach, wie der Name von NavLink zu ApplicationLink geändert wird, um den Anforderungen des Benutzers gerecht zu werden.
Neustyling von Navigation
Die Datei Navigation.tsx
wurde aktualisiert, indem NavLink
in ApplicationLink
umbenannt wurde. Das Interface ApplicationLink
beschreibt nun die Objektstruktur innerhalb des navLinks
-Arrays mit url
, title
und role
als string Typen.
Sicherstellen der Typensicherheit
TypeScript verwendet Interfaces, um sicherzustellen, dass alle ApplicationLink
-Objekte die erforderlichen Eigenschaften besitzen. Dies verhindert Laufzeitfehler durch fehlende oder falsch benannte Eigenschaften.
Natürlich! Hier ist die aktualisierte Navigation.tsx
-Datei mit dem Interface NavLink
umbenannt in ApplicationLink
. Anschließend erkläre ich, warum das Interface benötigt wird.
// Navigation.tsx import React from 'react'; import { tempUser } from './tempUser'; import { navLinks } from './navLinks'; interface ApplicationLink { url: string; title: string; role: string; } const renderNavLink = (link: ApplicationLink) => { // Überprüfen, ob die Rolle des Links im Rollen-Array des Benutzers vorhanden ist if (tempUser.roles.includes(link.role)) { return ( <li key={link.url}> <a href={link.url}>{link.title}</a> </li> ); } return null; // Link nicht anzeigen, wenn die Rolle nicht übereinstimmt }; const Navigation: React.FC = () => { return ( <nav> <ul> {navLinks.map(renderNavLink)} </ul> </nav> ); }; export default Navigation;
Das Interface ApplicationLink
definiert die Struktur der Link-Objekte innerhalb des navLinks
-Arrays. Es spezifiziert, welche Eigenschaften ein Link haben muss:
url: string
: Die URL, zu der der Link führt.title: string
: Der anzuzeigende Text des Links.role: string
: Die erforderliche Benutzerrolle, um den Link anzuzeigen.Typensicherheit: TypeScript überprüft anhand des Interfaces, ob die übergebenen Objekte die korrekten Eigenschaften mit den richtigen Typen haben. Das hilft, Fehler frühzeitig zu erkennen.
Code-Intelligenz: Editoren wie Visual Studio Code können bessere Autovervollständigung und Tooltips anbieten, was die Entwicklung effizienter macht.
Klare Dokumentation: Das Interface dient als Selbst-Dokumentation des Codes. Andere Entwickler (oder du selbst in der Zukunft) können schnell verstehen, welche Datenstruktur erwartet wird.
Wartbarkeit: Wenn sich die Struktur der Links ändert, muss nur das Interface angepasst werden, und TypeScript zeigt automatisch an, wo im Code Anpassungen erforderlich sind.
NavLink
in ApplicationLink
umbenennen?Vermeidung von Namenskonflikten: Der Begriff NavLink
wird häufig in Bibliotheken wie react-router-dom
verwendet. Durch die Umbenennung zu ApplicationLink
vermeidest du Verwechslungen oder Konflikte mit solchen Bibliotheken.
Klarheit: Der Name ApplicationLink
kann besser ausdrücken, dass es sich um Links innerhalb deiner spezifischen Anwendung handelt, die möglicherweise besondere Eigenschaften oder Anforderungen haben.
Mit dieser Anpassung ist deine Navigation.tsx
-Datei bereit für die Verwendung in einem Vite.js-Projekt. Das Extrahieren der Logik in eine separate Funktion renderNavLink
verbessert die Lesbarkeit und Wiederverwendbarkeit deines Codes.
Navigieren und Verknüpfen
Ich helfe beim Ansässigen einer Navigation in TypeScript und React. Dieser Abschnitt beleuchtet die Anpassung für Vite.js und das Extrahieren von Logik in eine separate Funktion.
Analyse und Anpassung
Der Benutzer möchte wissen, ob die Definition der Klasse ApplicationLink in Navigation.tsx noch notwendig ist, da die Definition der Klasse NavItem bereits in nav.ts zusammengeführt wurde.
Überprüfen des Codebedarfs
Ich schaue mir die bestehende NavItem-Schnittstelle an und prüfe, ob sie alle Anforderungen abdeckt. Falls ja, könnte der ApplicationLink in Navigation.tsx nicht mehr nötig sein.
Überprüfen und anwenden
Ich schaue mir an, ob ApplicationLink erforderlich ist und wie der Code angepasst wird. Importieren von NavItem
aus nav.ts
nach Navigation.tsx
scheint der richtige Weg zu sein.
Nebenrolle schätzen
Ich importiere NavItem
und passe renderNavLink
an, um NavItem
statt ApplicationLink
zu verwenden. Beachten Sie, dass NavItem
keine role
-Eigenschaft enthält, aber die ursprüngliche Überprüfung auf link.role
angewiesen ist.
Hinzufügen und anpassen
Ich füge das role
-Attribut zum NavItem
Interface hinzu, um sicherzustellen, dass es die Rollen für die Navigation repräsentiert und die Navigation verarbeiten kann.
Importieren und Anpassen
Ich importiere das NavItem
Interface in Navigation.tsx
und passe die Funktion renderNavLink
an, um link
als NavItem
zu typisieren.
Berücksichtigen optionaler Eigenschaften
Es tut mir leid für das Missverständnis. Lässt sich weiter erklären?
Da du bereits ein Interface NavItem
in deiner nav.ts
-Datei definiert hast, benötigst du das Interface ApplicationLink
in der Navigation.tsx
-Datei nicht mehr. Stattdessen kannst du das vorhandene NavItem
-Interface verwenden.
Allerdings musst du sicherstellen, dass das NavItem
-Interface alle Eigenschaften enthält, die du in der Navigation.tsx
verwendest – insbesondere die role
-Eigenschaft, die in deinem ursprünglichen Code für die Berechtigungsprüfung genutzt wird.
1. Importiere das NavItem
-Interface in der Navigation.tsx
-Datei:
import { NavItem } from './nav'; // Passe den Pfad entsprechend an
2. Stelle sicher, dass das NavItem
-Interface die role
-Eigenschaft enthält:
Da in deinem ursprünglichen NavItem
-Interface die role
-Eigenschaft nicht enthalten ist, solltest du sie hinzufügen:
// nav.ts export interface NavItem { title: string; title_en?: string; url: string; role?: string; // Füge die role-Eigenschaft hinzu badge_label?: string; icon?: LucideIcon; isActive?: boolean; app?: string; items?: SubItem[]; }
3. Passe die renderNavLink
-Funktion in der Navigation.tsx
-Datei an:
// Navigation.tsx import React from 'react'; import { tempUser } from './tempUser'; import { navLinks } from './navLinks'; // Passe den Importpfad an import { NavItem } from './nav'; // Importiere das NavItem-Interface const renderNavLink = (link: NavItem) => { // Überprüfe, ob die Rolle definiert ist und ob sie im Rollen-Array des Benutzers enthalten ist if (link.role && tempUser.roles.includes(link.role)) { return ( <li key={link.url}> <a href={link.url}>{link.title}</a> </li> ); } return null; // Link nicht anzeigen, wenn die Rolle nicht übereinstimmt oder nicht definiert ist }; const Navigation: React.FC = () => { return ( <nav> <ul> {navLinks.map(renderNavLink)} </ul> </nav> ); }; export default Navigation;
Warum wird das NavItem
-Interface verwendet?
Anpassung des NavItem
-Interfaces:
role
-Eigenschaft: Die role
-Eigenschaft ist notwendig, um die Berechtigungen für die Anzeige der Navigationslinks zu überprüfen. Sie sollte daher im Interface enthalten sein.Anpassung der renderNavLink
-Funktion:
role
-Eigenschaft: Da role
optional ist (mit ?
gekennzeichnet), musst du überprüfen, ob link.role
definiert ist, bevor du tempUser.roles.includes(link.role)
aufrufst, um Laufzeitfehler zu vermeiden.navLinks
, tempUser
und NavItem
korrekt sind und die Pfade stimmen.navLinks
: Achte darauf, dass die Elemente in navLinks
der Struktur von NavItem
entsprechen und die role
-Eigenschaft enthalten, falls erforderlich.Beispiel für navLinks
:
// navLinks.ts import { NavItem } from './nav'; export const navLinks: NavItem[] = [ { title: 'Dashboard', url: '/dashboard', role: 'admin', }, { title: 'Profil', url: '/profil', role: 'user', }, { title: 'Einstellungen', url: '/einstellungen', role: 'admin', }, // Weitere Links... ];
ApplicationLink
-Interface nicht mehr, da du das vorhandene NavItem
-Interface verwenden kannst.NavItem
-Interface und passe die renderNavLink
-Funktion an, um es zu verwenden.Navigation.tsx
-Datei// Navigation.tsx import React from 'react'; import { tempUser } from './tempUser'; import { navLinks } from './navLinks'; // Passe den Pfad an, falls erforderlich import { NavItem } from './nav'; // Importiere das NavItem-Interface const renderNavLink = (link: NavItem) => { // Überprüfe, ob die Rolle definiert ist und ob sie im Rollen-Array des Benutzers enthalten ist if (link.role && tempUser.roles.includes(link.role)) { return ( <li key={link.url}> <a href={link.url}>{link.title}</a> </li> ); } return null; // Link nicht anzeigen, wenn die Rolle nicht übereinstimmt oder nicht definiert ist }; const Navigation: React.FC = () => { return ( <nav> <ul> {navLinks.map(renderNavLink)} </ul> </nav> ); }; export default Navigation;
Durch die Anpassung deines Codes kannst du das bereits vorhandene NavItem
-Interface effektiv nutzen und musst kein zusätzliches Interface in der Navigation.tsx
-Datei definieren. Achte darauf, dass alle benötigten Eigenschaften im Interface enthalten sind und die Datentypen korrekt definiert sind.
Dies führt zu:
Solltest du weitere Fragen haben oder Hilfe bei weiteren Anpassungen benötigen, stehe ich gerne zur Verfügung!
Optimierung der Struktur
Der Benutzer arbeitet an einem TypeScript React-Code und wendet sich dabei an die Struktur von Next.js oder Vite.js. Es geht um bedingte Darstellung basierend auf Benutzerberechtigungen und die Extraktion des Innentextes einer map-Funktion in eine separate Funktion.
Analyzieren des Codes
Ich betrachte den bereitgestellten TypeScript-Code, der eine kollabierbare Seitenleiste mit Sidebar-Menüelementen und Submenüs darstellt. Haken dabei, dass es Interessant das Active-Nav-Funktionalität integriert.
Erstellen einer Funktion
Ich erstelle eine neue Funktion namens 'RenderApplicationLink', die den Renderprozess der Collapsible-Elemente abhängig von den Benutzerrechten handhabt.
Da stimme ich dem bei.
Erstellen einer neuen Funktion
Ich hefte mich an: Erstelle die Funktion RenderApplicationLink
und ordne den Rendercode für items.map
zu. Nur wenn item.app
vorhanden ist, bleibt der Abschnitt im DOM.
Implementierung einer Funktion
Natürlich! Ich werde den Code entsprechend anpassen und erklären, wie die Änderungen umgesetzt wurden.
nav-main-links.tsx
// nav-main-links.tsx import { ChevronRight, LayoutDashboard } from 'lucide-react'; import useCheckActiveNav from '@/hooks/use-check-active-nav'; import { NavItem } from '@/types/nav'; // Importiere die Typen import { tempUser } from '@/data/user-context-data/temp-user-1'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, } from '@/components/ui/sidebar'; interface NavMainLinksProps { items: NavItem[]; } export function NavMainLinks({ items }: NavMainLinksProps) { const { checkActiveNav } = useCheckActiveNav(); const RenderApplicationLink = (item: NavItem) => { // Überprüfe, ob der Nutzer Zugriff auf die Anwendung hat if (item.app && tempUser.apps && tempUser.apps.includes(item.app)) { return ( <Collapsible key={item.title} asChild defaultOpen={item.isActive} className="group/collapsible transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down" > <SidebarMenuItem> <CollapsibleTrigger asChild> <SidebarMenuButton isActive={checkActiveNav(item.url)} tooltip={item.title} className="data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > {item.icon && <item.icon className="opacity-50" />} <span>{item.title}</span> <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" /> </SidebarMenuButton> </CollapsibleTrigger> <CollapsibleContent> <SidebarMenuSub> {item.items?.map((subItem) => ( <SidebarMenuSubItem key={subItem.title}> <SidebarMenuSubButton isActive={checkActiveNav(subItem.url)} asChild className="hover:text-primary data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > <a href={subItem.url} className="gap-2"> {subItem.icon && ( <subItem.icon className="text-current" /> )} <span>{subItem.title}</span> </a> </SidebarMenuSubButton> </SidebarMenuSubItem> ))} </SidebarMenuSub> </CollapsibleContent> </SidebarMenuItem> </Collapsible> ); } // Wenn der Nutzer keine Berechtigung hat, wird nichts gerendert return null; }; return ( <SidebarGroup> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton asChild isActive={checkActiveNav('/')} tooltip={'Dashboard'} className="data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > <a href="./" className="gap-2"> <LayoutDashboard className="flex items-center gap-2" /> <span>Dashboard</span> </a> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> <SidebarGroupLabel>Anwendungen</SidebarGroupLabel> <SidebarMenu> {items.map(RenderApplicationLink)} </SidebarMenu> </SidebarGroup> ); }
map
-Funktionitems.map(...)
wurde in eine separate Funktion namens RenderApplicationLink
ausgelagert.Innerhalb der Funktion RenderApplicationLink
wird überprüft, ob der Nutzer die erforderlichen Rechte hat, um die Anwendung zu nutzen.
Die Überprüfung erfolgt mit dem folgenden Code:
if (item.app && tempUser.apps && tempUser.apps.includes(item.app)) { // Render das Collapsible Element }
Erläuterung:
item.app
: Prüft, ob das aktuelle Navigationselement einer bestimmten Anwendung zugeordnet ist.tempUser.apps
: Prüft, ob der Nutzer ein Array von Anwendungen hat, auf die er Zugriff hat.tempUser.apps.includes(item.app)
: Prüft, ob die Anwendung des Navigationselements in der Liste der Anwendungen des Nutzers enthalten ist.Wenn der Nutzer keine Berechtigung hat, wird null
zurückgegeben, und das Navigationselement wird nicht gerendert.
NavItem
-InterfacesNavItem
-Interface wird verwendet, um die Typen der Navigationselemente zu definieren.NavItem
die Eigenschaft app?: string
enthält, um die Anwendung zu identifizieren.NavItem
Interface in nav.ts
:
// nav.ts export interface NavItem { title: string; title_en?: string; url: string; badge_label?: string; icon?: LucideIcon; isActive?: boolean; app?: string; // Stellt sicher, dass 'app' vorhanden ist items?: SubItem[]; }
tempUser
Typ:
// temp-user-1.ts export interface User { // ... andere Eigenschaften apps?: string[]; // Liste der Anwendungen, auf die der Nutzer Zugriff hat } export const tempUser: User = { // ... andere Eigenschaften apps: ['app1', 'app2'], // Beispielanwendungen };
RenderApplicationLink
-Funktion kann leicht angepasst oder erweitert werden, ohne den Hauptcode zu beeinflussen.NavItem
-Interfaces und der Typen von tempUser
wird Typensicherheit gewährleistet.tempUser.apps
definiert ist, bevor includes
aufgerufen wird, um Laufzeitfehler zu vermeiden.?.
) könnte verwendet werden, um den Code noch robuster zu machen:
if (item.app && tempUser.apps?.includes(item.app)) { // ... }
app
oder apps
nicht definiert sind, wird das Navigationselement nicht gerendert, was ein sinnvolles Verhalten darstellt.// nav-main-links.tsx import { ChevronRight, LayoutDashboard } from 'lucide-react'; import useCheckActiveNav from '@/hooks/use-check-active-nav'; import { NavItem } from '@/types/nav'; import { tempUser } from '@/data/user-context-data/temp-user-1'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, } from '@/components/ui/sidebar'; interface NavMainLinksProps { items: NavItem[]; } export function NavMainLinks({ items }: NavMainLinksProps) { const { checkActiveNav } = useCheckActiveNav(); const RenderApplicationLink = (item: NavItem) => { // Überprüfe, ob der Nutzer Zugriff auf die Anwendung hat if (item.app && tempUser.apps && tempUser.apps.includes(item.app)) { return ( <Collapsible key={item.title} asChild defaultOpen={item.isActive} className="group/collapsible transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down" > <SidebarMenuItem> <CollapsibleTrigger asChild> <SidebarMenuButton isActive={checkActiveNav(item.url)} tooltip={item.title} className="data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > {item.icon && <item.icon className="opacity-50" />} <span>{item.title}</span> <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" /> </SidebarMenuButton> </CollapsibleTrigger> <CollapsibleContent> <SidebarMenuSub> {item.items?.map((subItem) => ( <SidebarMenuSubItem key={subItem.title}> <SidebarMenuSubButton isActive={checkActiveNav(subItem.url)} asChild className="hover:text-primary data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > <a href={subItem.url} className="gap-2"> {subItem.icon && ( <subItem.icon className="text-current" /> )} <span>{subItem.title}</span> </a> </SidebarMenuSubButton> </SidebarMenuSubItem> ))} </SidebarMenuSub> </CollapsibleContent> </SidebarMenuItem> </Collapsible> ); } // Wenn der Nutzer keine Berechtigung hat, wird nichts gerendert return null; }; return ( <SidebarGroup> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton asChild isActive={checkActiveNav('/')} tooltip={'Dashboard'} className="data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > <a href="./" className="gap-2"> <LayoutDashboard className="flex items-center gap-2" /> <span>Dashboard</span> </a> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> <SidebarGroupLabel>Anwendungen</SidebarGroupLabel> <SidebarMenu> {items.map(RenderApplicationLink)} </SidebarMenu> </SidebarGroup> ); }
RenderApplicationLink
: Enthält die Logik zum Rendern eines einzelnen Navigationselements basierend auf den Nutzerrechten.subItem
) ebenfalls Berechtigungen erfordern, könntest du ähnliche Überprüfungen innerhalb des item.items?.map(...)
durchführen.React.memo
in Betracht gezogen werden.Mit diesen Anpassungen erfüllt der Code deine Anforderungen:
RenderApplicationLink
für bessere Lesbarkeit und Wartbarkeit.Solltest du weitere Fragen haben oder zusätzliche Anpassungen benötigen, stehe ich gerne zur Verfügung!
Ändern des Codes
Ich beginne, die logik in der Funktion "RenderApplicationLink" zu ändern, um den richtigen "SidebarMenuSubItem" anzuzeigen. Dabei benutze ich "tempUser.rights.includes(item.right)" und extrahiere diese Logik in eine neue Funktion namens RenderModuleRight.
Anpassungen im Detail
Ich arbeite daran, RenderApplicationLink so anzupassen, dass innerhalb von item.items Bedingung rendern mit item.right. Dies erfordert eine sorgfältige Überprüfung und Überarbeitung des Codes.
Entwickeln der Funktion
Ich plane, die Anforderung zu erfüllen, indem ich die Logik in einer Funktion namens RenderModuleRight extrahiere und sicherstelle, dass <SidebarMenuSubItem> nur gerendert wird, wenn tempUser.rights includes subItem.right.
Anpassen und optimieren
Ich schaue mir den Code an, um ihn fein abzustimmen und die Funktionen für Benutzerzugriffe und Sidebar-Links zu verbessern.
Überarbeiten des Codes
Ich arbeite daran, die Funktion RenderApplicationLink zu verbessern, indem ich den Code für die Zuweisung von <SidebarMenuSubItem> basierend auf tempUser.rights in die Funktion RenderModuleRight verschiebe.
Überprüfen der Berechtigungen: Ich schaue mir an, ob 'subItem.right', 'tempUser.rights' und 'subItem.right' über Bearbeitungseinstellungen in der Datenbank oder Tabelle verfügen.
Feldzugang sicherstellen
Ich entwickle RenderModuleRight, um sicherzustellen, dass Benutzerrechte überprüft werden, bevor ein Element angezeigt wird. Nur bei vorhandenem Recht und passendem User-Daten wird das Element sichtbar.
Importieren und definieren
Ich habe SubItem aus '@/types/nav' importiert und RenderModuleRight funktional definiert, um Unterelemente zu überprüfen und anzuzeigen.
Überprüfen auf sichtbare Elemente
Ich überlege, wie man sicherstellen kann, dass der Collapsible nur angezeigt wird, wenn mindestens ein sichtbares Unterelement vorhanden ist. Möglicherweise filtere ich die Elemente oder verwende filter(Boolean)
.
Überprüfen und anpassen
Ich schaue mir an, ob der Benutzer Zugriff auf die Anwendung hat. Dann prüfe ich, ob es Unterpunkte gibt, und rendere den Collapsible nur, wenn Unterpunkte vorhanden sind.
Code-Anpassung überlegen
Ich denke darüber nach, wie der angepasste Code, einschließlich der neuen RenderModuleRight-Funktion, strukturiert sein sollte. Interessant ist auch, ob die Collapsible-Funktion nur gerendert wird, wenn Unterelemente vorhanden sind.
Bereitstellen des Codes
Der Benutzer wünscht den Code, woraufhin der Assistent diesen bereitstellt.
Organisieren des Codes
Ich habe die Funktion RenderModuleRight
ausgelagert und die Navigationsstruktur überarbeitet, um die Lesbarkeit zu verbessern und sicherzustellen, dass der Benutzer nur auf Module zugreifen kann, wenn er die entsprechenden Rechte besitzt.
Überprüfen und Filtern
Ich schaue mir die Benutzerrechte an und filtere die Sub-Elemente, um nur die anzuzeigen, zu denen der Benutzer Zugriff hat.
Verhindern leerer Links
Ich denke darüber nach, leere Links in der Anwendung zu verhindern. Dies geschieht durch das Rendern des Collapsible-Elements nur, wenn mindestens ein Unterelement vorhanden ist.
Zusammenführung klarer Funktionen
Ich extrahiere die Funktionen RenderModuleRight
und RenderApplicationLink
, um den Code besser zu strukturieren. Diese Funktionen haben jetzt jeweils eine klare Aufgabe, was die Wartung und Verständlichkeit verbessert.
Administrieren von Benutzerlinks
Ich habe implementiert, dass der NavMainLinks
-Komponent Links zu Anwendungen nur dann anzeigt, wenn der Benutzer darauf zugreifen kann, und entsprechende Unterelemente auswählt.
Natürlich! Hier ist der angepasste Code für nav-main-links.tsx
, bei dem das Element <SidebarMenuSubItem>
innerhalb der map
-Methode nur dann angezeigt wird, wenn die Rechte des Nutzers mit den Rechten des Links übereinstimmen. Ich habe dazu die Logik in die Funktion RenderModuleRight
ausgelagert und die Struktur des Dokuments verbessert.
// nav-main-links.tsx import { ChevronRight, LayoutDashboard } from 'lucide-react'; import useCheckActiveNav from '@/hooks/use-check-active-nav'; import { NavItem, SubItem } from '@/types/nav'; // Importiere die Typen import { tempUser } from '@/data/user-context-data/temp-user-1'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; import { SidebarGroup, SidebarGroupLabel, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, } from '@/components/ui/sidebar'; interface NavMainLinksProps { items: NavItem[]; } export function NavMainLinks({ items }: NavMainLinksProps) { const { checkActiveNav } = useCheckActiveNav(); // Funktion zum Rendern der Module basierend auf den Nutzerrechten const RenderModuleRight = (subItem: SubItem) => { if (subItem.right && tempUser.rights && tempUser.rights.includes(subItem.right)) { return ( <SidebarMenuSubItem key={subItem.title}> <SidebarMenuSubButton isActive={checkActiveNav(subItem.url)} asChild className="hover:text-primary data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > <a href={subItem.url} className="gap-2"> {subItem.icon && ( <subItem.icon className="text-current" /> )} <span>{subItem.title}</span> </a> </SidebarMenuSubButton> </SidebarMenuSubItem> ); } // Nicht rendern, wenn der Nutzer keine Berechtigung hat return null; }; // Funktion zum Rendern der Applikationslinks basierend auf den Nutzerrechten const RenderApplicationLink = (item: NavItem) => { // Überprüfe, ob der Nutzer Zugriff auf die Anwendung hat if (item.app && tempUser.apps && tempUser.apps.includes(item.app)) { // Rendern der Module und Filtern von null-Werten const renderedSubItems = item.items?.map(RenderModuleRight).filter(Boolean); // Nur rendern, wenn es Module gibt, auf die der Nutzer Zugriff hat if (renderedSubItems && renderedSubItems.length > 0) { return ( <Collapsible key={item.title} asChild defaultOpen={item.isActive} className="group/collapsible transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down" > <SidebarMenuItem> <CollapsibleTrigger asChild> <SidebarMenuButton isActive={checkActiveNav(item.url)} tooltip={item.title} className="data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > {item.icon && <item.icon className="opacity-50" />} <span>{item.title}</span> <ChevronRight className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" /> </SidebarMenuButton> </CollapsibleTrigger> <CollapsibleContent> <SidebarMenuSub> {renderedSubItems} </SidebarMenuSub> </CollapsibleContent> </SidebarMenuItem> </Collapsible> ); } } // Nicht rendern, wenn der Nutzer keine Berechtigung hat oder keine Module verfügbar sind return null; }; return ( <SidebarGroup> <SidebarMenu> <SidebarMenuItem> <SidebarMenuButton asChild isActive={checkActiveNav('/')} tooltip={'Dashboard'} className="data-[active=true]:border data-[active=true]:border-primary/20 data-[active=true]:bg-blue-50 data-[active=true]:text-primary" > <a href="./" className="gap-2"> <LayoutDashboard className="flex items-center gap-2" /> <span>Dashboard</span> </a> </SidebarMenuButton> </SidebarMenuItem> </SidebarMenu> <SidebarGroupLabel>Anwendungen</SidebarGroupLabel> <SidebarMenu> {items.map(RenderApplicationLink)} </SidebarMenu> </SidebarGroup> ); }
Auslagern der Logik in RenderModuleRight
:
RenderModuleRight
rendert jedes Modul (subItem
) nur dann, wenn der Nutzer die entsprechenden Rechte besitzt.if (subItem.right && tempUser.rights && tempUser.rights.includes(subItem.right)) { // Render das Modul }
null
zurückgegeben, und das Modul wird nicht angezeigt.Anpassung von RenderApplicationLink
:
RenderApplicationLink
werden die Module mit RenderModuleRight
gerendert und anschließend gefiltert, um null
-Werte zu entfernen:
const renderedSubItems = item.items?.map(RenderModuleRight).filter(Boolean);
renderedSubItems
vorhanden sind, bevor das Collapsible-Element gerendert wird. Dadurch wird verhindert, dass Anwendungen ohne zugängliche Module angezeigt werden.Verbesserte Struktur des Codes:
RenderModuleRight
: Rendert einzelne Module basierend auf den Nutzerrechten.RenderApplicationLink
: Rendert Anwendungen, wenn der Nutzer Zugriff hat, und enthält die gerenderten Module.Typen und Interfaces:
NavItem
Interface:
items?: SubItem[];
enthalten.SubItem
Interface:
right?: string;
enthalten, um die erforderlichen Rechte zu definieren.export interface SubItem { title: string; url: string; right?: string; icon?: LucideIcon; // Weitere Eigenschaften... }
tempUser
Objekt:
rights?: string[];
enthalten, das die Rechte des Nutzers definiert.items
und tempUser
korrekt strukturiert sind und die benötigten Eigenschaften enthalten.tempUser
:
export const tempUser = { apps: ['app1', 'app2'], rights: ['read', 'write', 'execute'], // Weitere Eigenschaften... };
?
) und optional chaining (?.
) wird sichergestellt, dass der Code robust gegenüber fehlenden Werten ist.null
-Werten:
.filter(Boolean)
entfernt alle null
und undefined
Werte aus dem Array, sodass nur tatsächlich gerenderte Module übrigbleiben.Mit diesen Anpassungen wird das <SidebarMenuSubItem>
innerhalb der map
-Methode nur dann angezeigt, wenn der Nutzer die entsprechenden Rechte besitzt. Die Logik wurde in die Funktion RenderModuleRight
ausgelagert, um den Code besser zu strukturieren und die Lesbarkeit zu verbessern.
Ich hoffe, dass diese Anpassungen deinen Anforderungen entsprechen. Wenn du weitere Fragen hast oder zusätzliche Hilfe benötigst, stehe ich gerne zur Verfügung!