Struktura projektu React / Next.js (App Router)
Frontend ZEUS używa Next.js 15 z App Routerem. Spójna struktura katalogów i jasna granica między komponentami serwerowymi a klienckimi to fundament, na którym budujemy resztę.
Struktura katalogów
app/
├── (dashboard)/
│ ├── layout.tsx
│ └── probes/
│ ├── page.tsx # Server Component (domyślnie)
│ └── probe-table.tsx # "use client" — interaktywny
├── api/
│ └── probes/route.ts # Route Handler
├── layout.tsx
└── globals.css
components/ # współdzielone, reużywalne UI
lib/ # logika, klienci API, utils
hooks/ # custom hooks (use client)
types/ # współdzielone typy
Standard ProfessNet: katalog
app/to wyłącznie routing i kompozycja stron. Reużywalne UI mieszka wcomponents/, logika wlib/.page.tsxnie zawiera ciężkiej logiki — woła funkcje zlib/.
Server vs Client Components
W App Routerze komponenty są domyślnie serwerowe. Renderują się na serwerze, nie trafiają do bundla i mogą czytać dane bezpośrednio.
// app/(dashboard)/probes/page.tsx — Server Component
import { getProbes } from "@/lib/probes";
import { ProbeTable } from "./probe-table";
export default async function ProbesPage() {
const probes = await getProbes(); // fetch po stronie serwera
return <ProbeTable probes={probes} />;
}
"use client" dodajemy tylko tam, gdzie potrzeba interaktywności — stanu,
eventów, hooków przeglądarki.
// app/(dashboard)/probes/probe-table.tsx
"use client";
import { useState } from "react";
import type { Probe } from "@/types/probe";
export function ProbeTable({ probes }: { probes: Probe[] }) {
const [filter, setFilter] = useState("");
const shown = probes.filter((p) => p.host.includes(filter));
return (
<>
<input value={filter} onChange={(e) => setFilter(e.target.value)} />
{shown.map((p) => (
<Row key={p.id} probe={p} />
))}
</>
);
}
Standard ProfessNet:
"use client"zjeżdża jak najniżej w drzewie. Nie oznaczaj całej strony jako klienckiej tylko dla jednego przycisku — wydzielisz mały komponent kliencki i resztę zostawiasz na serwerze.
Konwencje nazewnictwa
| Element | Konwencja | Przykład |
|---|---|---|
| Komponent (plik) | kebab-case | probe-table.tsx |
| Komponent (eksport) | PascalCase | ProbeTable |
| Hook | use + camelCase | useStudioData |
| Typ / interfejs | PascalCase | Probe, ScanResult |
| Pliki specjalne Next | zarezerwowane | page, layout, loading, error |
Importy z aliasem
Konfigurujemy alias @/ zamiast względnych ../../../.
{ "compilerOptions": { "paths": { "@/*": ["./*"] } } }
import { ProbeTable } from "@/components/probe-table"; // czytelnie
Wskazówka: pliki
loading.tsxierror.tsxw segmencie dają automatycznie stany ładowania i błędu dla całej podstrony — wykorzystuj je zamiast ręcznych spinnerów wszędzie.
Domyślnie serwerowo, klient tylko tam, gdzie trzeba, logika w lib/ — to
struktura, która skaluje się razem z konsolą ZEUS i utrzymuje mały bundel po
stronie przeglądarki.