375 lines
10 KiB
Markdown
375 lines
10 KiB
Markdown
# shadcn/ui and React Router Implementation Guide
|
|
|
|
This document outlines how shadcn/ui components and React Router are implemented in the Inogen project.
|
|
|
|
## 📋 Overview
|
|
|
|
The project has been successfully updated to use:
|
|
- **shadcn/ui components** for consistent, accessible UI elements
|
|
- **React Router v7** for client-side routing and navigation
|
|
|
|
## 🎨 shadcn/ui Implementation
|
|
|
|
### Available Components
|
|
|
|
The project includes the following shadcn/ui components:
|
|
|
|
```
|
|
inogen/app/components/ui/
|
|
├── button.tsx # Button component with variants
|
|
├── card.tsx # Card, CardHeader, CardContent, etc.
|
|
├── input.tsx # Form input component
|
|
├── label.tsx # Form label component
|
|
└── design-system.tsx
|
|
```
|
|
|
|
### Configuration
|
|
|
|
shadcn/ui is configured via `components.json`:
|
|
|
|
```json
|
|
{
|
|
"$schema": "https://ui.shadcn.com/schema.json",
|
|
"style": "new-york",
|
|
"rsc": false,
|
|
"tsx": true,
|
|
"tailwind": {
|
|
"config": "",
|
|
"css": "app/app.css",
|
|
"baseColor": "zinc",
|
|
"cssVariables": true,
|
|
"prefix": ""
|
|
},
|
|
"aliases": {
|
|
"components": "~/components",
|
|
"utils": "~/lib/utils",
|
|
"ui": "~/components/ui"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Login Form Implementation
|
|
|
|
The login form has been refactored to use shadcn/ui components:
|
|
|
|
**Before (Plain HTML):**
|
|
```tsx
|
|
<input
|
|
className="w-full px-4 py-3 border border-gray-300..."
|
|
placeholder="نام کاربری"
|
|
/>
|
|
<button className="w-full bg-green-500...">
|
|
ورود
|
|
</button>
|
|
```
|
|
|
|
**After (shadcn/ui):**
|
|
```tsx
|
|
import { Button } from "~/components/ui/button";
|
|
import { Input } from "~/components/ui/input";
|
|
import { Label } from "~/components/ui/label";
|
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "~/components/ui/card";
|
|
|
|
<Card className="shadow-xl">
|
|
<CardHeader>
|
|
<CardTitle className="font-persian">ورود به سیستم</CardTitle>
|
|
<CardDescription className="font-persian">
|
|
لطفاً اطلاعات ورود خود را وارد کنید
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<Label htmlFor="username" className="font-persian">نام کاربری</Label>
|
|
<Input
|
|
id="username"
|
|
value={username}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
className="font-persian text-right"
|
|
placeholder="نام کاربری خود را وارد کنید"
|
|
/>
|
|
<Button variant="green" size="lg" className="w-full font-persian">
|
|
ورود
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
```
|
|
|
|
### Button Variants
|
|
|
|
The Button component includes custom variants for the project:
|
|
|
|
```tsx
|
|
variant: {
|
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
link: "text-primary underline-offset-4 hover:underline",
|
|
green: "bg-green-500 text-white hover:bg-green-600 focus:ring-green-400", // Custom
|
|
blue: "bg-blue-500 text-white hover:bg-blue-600 focus:ring-blue-400", // Custom
|
|
}
|
|
```
|
|
|
|
## 🚀 React Router Implementation
|
|
|
|
### Version & Configuration
|
|
|
|
- **React Router v7.7.0** is used throughout the project
|
|
- SSR is enabled by default in `react-router.config.ts`
|
|
|
|
### Route Structure
|
|
|
|
```typescript
|
|
// app/routes.ts
|
|
export default [
|
|
index("routes/home.tsx"), // /
|
|
route("login", "routes/login.tsx"), // /login
|
|
route("dashboard", "routes/dashboard.tsx"), // /dashboard
|
|
route("dashboard/projects", "routes/dashboard.projects.tsx"), // /dashboard/projects
|
|
route("404", "routes/404.tsx"), // /404
|
|
route("unauthorized", "routes/unauthorized.tsx"), // /unauthorized
|
|
route("*", "routes/$.tsx"), // Catch-all for 404s
|
|
] satisfies RouteConfig;
|
|
```
|
|
|
|
### Navigation Implementation
|
|
|
|
#### 1. Basic Navigation with Link
|
|
|
|
```tsx
|
|
import { Link } from "react-router";
|
|
|
|
<Link
|
|
to="/forgot-password"
|
|
className="text-green-600 hover:text-green-500 font-persian"
|
|
>
|
|
رمز عبور خود را فراموش کردهاید؟
|
|
</Link>
|
|
```
|
|
|
|
#### 2. Programmatic Navigation
|
|
|
|
```tsx
|
|
import { useNavigate } from "react-router";
|
|
|
|
const navigate = useNavigate();
|
|
|
|
// Navigate to dashboard after login
|
|
const handleLoginSuccess = () => {
|
|
navigate("/dashboard", { replace: true });
|
|
};
|
|
|
|
// Navigate back
|
|
const handleGoBack = () => {
|
|
navigate(-1);
|
|
};
|
|
```
|
|
|
|
#### 3. Active Link Styling
|
|
|
|
```tsx
|
|
import { Link, useLocation } from "react-router";
|
|
|
|
function NavigationLink({ to, label }: NavigationLinkProps) {
|
|
const location = useLocation();
|
|
const isActive = location.pathname === to;
|
|
|
|
return (
|
|
<Link
|
|
to={to}
|
|
className={`px-3 py-2 rounded-md font-persian ${
|
|
isActive
|
|
? "bg-green-100 text-green-700 dark:bg-green-900 dark:text-green-300"
|
|
: "text-gray-600 hover:text-gray-900 dark:text-gray-300"
|
|
}`}
|
|
>
|
|
{label}
|
|
</Link>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Route Protection
|
|
|
|
The project implements comprehensive route protection:
|
|
|
|
#### 1. Protected Route Component
|
|
|
|
```tsx
|
|
// app/components/auth/protected-route.tsx
|
|
export function ProtectedRoute({ children, requireAuth = true }: ProtectedRouteProps) {
|
|
const { isAuthenticated, isLoading } = useAuth();
|
|
const location = useLocation();
|
|
|
|
if (isLoading) {
|
|
return <LoadingSpinner />;
|
|
}
|
|
|
|
if (requireAuth && !isAuthenticated) {
|
|
const returnTo = location.pathname + location.search;
|
|
const loginPath = `/login?returnTo=${encodeURIComponent(returnTo)}`;
|
|
return <Navigate to={loginPath} replace />;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|
|
```
|
|
|
|
#### 2. Global Route Guard
|
|
|
|
```tsx
|
|
// app/components/auth/global-route-guard.tsx
|
|
export function GlobalRouteGuard({ children }: GlobalRouteGuardProps) {
|
|
const { isAuthenticated, isLoading, token } = useAuth();
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
|
|
useEffect(() => {
|
|
// Handle authentication-based redirects
|
|
if (!isLoading) {
|
|
handleRouteProtection();
|
|
}
|
|
}, [isAuthenticated, isLoading, location.pathname]);
|
|
|
|
return <>{children}</>;
|
|
}
|
|
```
|
|
|
|
### Advanced Navigation Features
|
|
|
|
#### 1. Return URL Handling
|
|
|
|
```tsx
|
|
// Login page automatically redirects to intended destination
|
|
const [searchParams] = useSearchParams();
|
|
const returnTo = searchParams.get("returnTo");
|
|
|
|
useEffect(() => {
|
|
if (isAuthenticated && !isLoading) {
|
|
const redirectPath = returnTo && returnTo !== "/login" ? returnTo : "/dashboard";
|
|
navigate(redirectPath, { replace: true });
|
|
}
|
|
}, [isAuthenticated, isLoading, navigate, returnTo]);
|
|
```
|
|
|
|
#### 2. Dashboard Navigation Menu
|
|
|
|
```tsx
|
|
// app/components/dashboard/dashboard-layout.tsx
|
|
<nav className="hidden md:flex items-center space-x-8 space-x-reverse">
|
|
<NavigationLink to="/dashboard" label="داشبورد" />
|
|
<NavigationLink to="/dashboard/projects" label="پروژهها" />
|
|
</nav>
|
|
```
|
|
|
|
## 🔒 Authentication Integration
|
|
|
|
### Auth Context with Router
|
|
|
|
```tsx
|
|
// app/contexts/auth-context.tsx
|
|
export function AuthProvider({ children }: AuthProviderProps) {
|
|
const [user, setUser] = useState<User | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
// Auto-validate tokens and handle expired sessions
|
|
useEffect(() => {
|
|
const interval = setInterval(async () => {
|
|
const isValid = await validateToken();
|
|
if (!isValid) {
|
|
clearAuthData();
|
|
toast.error("جلسه کاری شما منقضی شده است");
|
|
// Router will handle redirect via GlobalRouteGuard
|
|
}
|
|
}, 5 * 60 * 1000); // Every 5 minutes
|
|
|
|
return () => clearInterval(interval);
|
|
}, [token, user]);
|
|
|
|
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
|
}
|
|
```
|
|
|
|
## 📱 Responsive Design
|
|
|
|
Both shadcn/ui components and React Router navigation are fully responsive:
|
|
|
|
```tsx
|
|
{/* Mobile-friendly navigation */}
|
|
<nav className="hidden md:flex items-center space-x-8 space-x-reverse">
|
|
<NavigationLink to="/dashboard" label="داشبورد" />
|
|
<NavigationLink to="/dashboard/projects" label="پروژهها" />
|
|
</nav>
|
|
|
|
{/* Mobile logo for small screens */}
|
|
<div className="lg:hidden text-center mb-8">
|
|
<div className="w-16 h-16 bg-green-500 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
{/* Mobile logo */}
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
## 🎯 Benefits Achieved
|
|
|
|
### shadcn/ui Benefits:
|
|
- ✅ Consistent design system
|
|
- ✅ Accessible components by default
|
|
- ✅ Customizable with Tailwind CSS
|
|
- ✅ TypeScript support
|
|
- ✅ Reduced boilerplate code
|
|
|
|
### React Router Benefits:
|
|
- ✅ Client-side routing (SPA experience)
|
|
- ✅ Programmatic navigation
|
|
- ✅ Route protection and guards
|
|
- ✅ URL state management
|
|
- ✅ Return URL handling
|
|
- ✅ Active link styling
|
|
- ✅ SEO-friendly with SSR support
|
|
|
|
## 🛠️ Next Steps
|
|
|
|
To further enhance the implementation:
|
|
|
|
1. **Add more shadcn/ui components** (Dialog, DropdownMenu, Sheet for mobile nav)
|
|
2. **Implement breadcrumb navigation** using React Router location
|
|
3. **Add loading states** for route transitions
|
|
4. **Create reusable navigation components** for different sections
|
|
5. **Add route-based animations** using Framer Motion
|
|
|
|
## 📝 Usage Examples
|
|
|
|
### Creating a New Protected Page
|
|
|
|
```tsx
|
|
// app/routes/new-page.tsx
|
|
import { ProtectedRoute } from "~/components/auth/protected-route";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
|
|
|
|
export default function NewPage() {
|
|
return (
|
|
<ProtectedRoute>
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="font-persian">صفحه جدید</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="font-persian">محتوای صفحه جدید</p>
|
|
</CardContent>
|
|
</Card>
|
|
</ProtectedRoute>
|
|
);
|
|
}
|
|
```
|
|
|
|
### Adding Navigation Link
|
|
|
|
```tsx
|
|
// Add to routes.ts
|
|
route("new-page", "routes/new-page.tsx"),
|
|
|
|
// Add to dashboard navigation
|
|
<NavigationLink to="/new-page" label="صفحه جدید" />
|
|
```
|
|
|
|
This implementation provides a solid foundation for scalable, maintainable React Router navigation with consistent shadcn/ui components throughout the application. |