Aggiungere una Nuova Route
Guida step-by-step per creare nuove route in elerama-frontend con React Router 7.
Panoramica
Una route completa richiede:
- Route Component - Componente React per la pagina
- Registrazione Route - Entry in
routes.ts - Protezione - clientLoader per autenticazione
- Meta Tags - Titolo e descrizione pagina
Struttura File
app/
├── routes/
│ ├── auth/ # Route autenticazione (login, companies)
│ ├── admin/ # Route amministrative
│ │ └── {modulo}/ # Pagine modulo (es: brands/)
│ │ └── index.tsx # Pagina principale
│ └── r.$path.tsx # Route dinamica per moduli ERP iframe
└── routes.ts # Registrazione routeTipi di Route
1. Route Protetta (Autenticata)
La maggior parte delle route dell'applicazione richiede autenticazione.
typescript
// app/routes/admin/example/index.tsx
import { AuthenticatedLayout } from "@/components/layout/authenticated";
import { createProtectedLoader } from "@/lib/auth/loaders";
// Meta tags per la pagina
export function meta() {
return [
{ title: "Esempio - Elerama" },
{ name: "description", content: "Descrizione pagina" },
];
}
// Protezione route: richiede autenticazione
export const clientLoader = createProtectedLoader();
export default function ExamplePage() {
return (
<AuthenticatedLayout>
<div className="p-6">
<h1>Contenuto pagina</h1>
</div>
</AuthenticatedLayout>
);
}2. Route Protetta con Settings
Per route che necessitano dei settings utente (menu, permessi, ecc.).
typescript
// app/routes/admin/brands/index.tsx
import { AuthenticatedLayout } from "@/components/layout/authenticated";
import { createProtectedLoader } from "@/lib/auth/loaders";
export function meta() {
return [
{ title: "Gestione Brand - Elerama" },
{ name: "description", content: "Gestione anagrafica brand" },
];
}
// Protezione route: richiede autenticazione + settings caricati
export const clientLoader = createProtectedLoader({ requireSettings: true });
export default function BrandsPage() {
// Settings garantiti dal loader
return (
<AuthenticatedLayout>
{/* Contenuto */}
</AuthenticatedLayout>
);
}3. Route Pubblica (Guest Only)
Per route accessibili solo a utenti non autenticati (login, registrazione).
typescript
// app/routes/auth/login.tsx
import { createGuestLoader } from "@/lib/auth/loaders";
export function meta() {
return [{ title: "Login - Elerama" }];
}
// Solo utenti non autenticati (redirect se già loggati)
export const clientLoader = createGuestLoader();
export default function LoginPage() {
return (
<div>
{/* Form login */}
</div>
);
}Registrazione Route
File: app/routes.ts
typescript
import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
// Route index (login)
index("routes/auth/login.tsx"),
// Route post-login
route("companies", "routes/auth/companies.tsx"),
route("confirm-login", "routes/auth/confirm-login.tsx"),
route("welcome", "routes/welcome.tsx"),
// Route admin
route("admin/brands", "routes/admin/brands/index.tsx"),
route("admin/example", "routes/admin/example/index.tsx"),
// Route dinamica per moduli ERP (iframe)
route("r/*", "routes/r.$path.tsx"),
// Catch-all 404 (deve essere l'ultima)
route("*", "routes/$.tsx"),
] satisfies RouteConfig;Convenzioni Path
| Tipo | Path | Esempio |
|---|---|---|
| Admin | admin/{modulo} | admin/brands |
| Auth | {action} | login, companies |
| ERP Iframe | r/{path} | r/navigator, r/products/list |
Opzioni createProtectedLoader
typescript
interface ProtectedLoaderOptions {
/**
* Verifica che i settings siano caricati.
* Se non presenti, li carica automaticamente.
* Usa per route che necessitano di menu/permessi.
*/
requireSettings?: boolean;
/**
* Carica i settings prima del render.
* Blocca finché non sono disponibili.
* Usa per pagine iniziali (welcome).
*/
loadSettings?: boolean;
/**
* Se false, non redirige automaticamente.
* @default true
*/
redirect?: boolean;
}Esempi d'Uso
typescript
// Solo autenticazione
export const clientLoader = createProtectedLoader();
// Autenticazione + settings (route admin)
export const clientLoader = createProtectedLoader({ requireSettings: true });
// Carica settings (welcome page)
export const clientLoader = createProtectedLoader({ loadSettings: true });Layout Comuni
AuthenticatedLayout
Layout standard per pagine autenticate con sidebar e topbar.
typescript
import { AuthenticatedLayout } from "@/components/layout/authenticated";
export default function MyPage() {
return (
<AuthenticatedLayout>
<div className="p-6">
{/* Contenuto con padding standard */}
</div>
</AuthenticatedLayout>
);
}Card Layout Pattern
Pattern comune per pagine con contenuto centrato in card.
typescript
import { AuthenticatedLayout } from "@/components/layout/authenticated";
import { Card, CardContent, CardHeader, CardTitle } from "@elerama/ui";
export default function MyPage() {
return (
<AuthenticatedLayout>
<div className="p-6">
<Card>
<CardHeader>
<CardTitle>Titolo</CardTitle>
</CardHeader>
<CardContent>
{/* Contenuto */}
</CardContent>
</Card>
</div>
</AuthenticatedLayout>
);
}Pattern Data Fetching
Con TanStack Query
Pattern raccomandato per tutte le route con dati server.
typescript
import { useMyDataList } from "@/hooks/api/mymodule/useMyModuleApi";
export default function MyPage() {
const { data, isLoading, error, refetch } = useMyDataList();
// Gestione errore
if (error) {
return (
<AuthenticatedLayout>
<Alert variant="destructive">
<AlertTitle>Errore</AlertTitle>
<AlertDescription>{error.message}</AlertDescription>
<Button onClick={() => refetch()}>Riprova</Button>
</Alert>
</AuthenticatedLayout>
);
}
return (
<AuthenticatedLayout>
{isLoading ? (
<Spinner />
) : (
<div>{/* Render dati */}</div>
)}
</AuthenticatedLayout>
);
}Esempio Completo: Pagina Admin CRUD
typescript
// app/routes/admin/products/index.tsx
import { AuthenticatedLayout } from "@/components/layout/authenticated";
import { useProductsList, useDeleteProduct } from "@/hooks/api/products/useProductsApi";
import { createProtectedLoader } from "@/lib/auth/loaders";
import {
Alert,
AlertDescription,
AlertTitle,
Button,
Card,
CardContent,
CardHeader,
CardTitle,
useToast,
} from "@elerama/ui";
export function meta() {
return [
{ title: "Gestione Prodotti - Elerama" },
{ name: "description", content: "Gestione anagrafica prodotti" },
];
}
export const clientLoader = createProtectedLoader({ requireSettings: true });
export default function ProductsPage() {
const { success: showSuccess, error: showError } = useToast();
// Query lista
const { data: products, isLoading, error, refetch } = useProductsList();
// Mutation eliminazione
const { mutate: deleteProduct, isPending } = useDeleteProduct({
onSuccess: () => showSuccess("Prodotto eliminato"),
onError: (err) => showError(err.message),
});
if (error) {
return (
<AuthenticatedLayout>
<div className="flex flex-1 items-center justify-center p-6">
<Alert variant="destructive" className="max-w-md">
<AlertTitle>Errore nel caricamento</AlertTitle>
<AlertDescription>{error.message}</AlertDescription>
<Button onClick={() => refetch()} className="mt-4">
Riprova
</Button>
</Alert>
</div>
</AuthenticatedLayout>
);
}
return (
<AuthenticatedLayout>
<div className="p-6">
<Card>
<CardHeader>
<CardTitle>Gestione Prodotti</CardTitle>
</CardHeader>
<CardContent>
{isLoading ? (
<div>Caricamento...</div>
) : (
<div>
{/* Lista prodotti */}
</div>
)}
</CardContent>
</Card>
</div>
</AuthenticatedLayout>
);
}Checklist
- [ ] Componente route in
app/routes/ - [ ] Funzione
meta()con title e description - [ ]
clientLoadercon protezione appropriata - [ ] Layout corretto (
AuthenticatedLayouto altro) - [ ] Route registrata in
routes.ts - [ ] Gestione stati: loading, error, empty
- [ ] Data fetching con TanStack Query hooks
Riferimenti
- Nuovo Modulo API - Come creare API hooks
- Component Patterns - Pattern UI comuni
- TanStack Query Guide - Data fetching