FR
frontend-architecture
Frontend architecture and design patterns for SeShop. Use when: understanding React component structure, designing state management, integrating APIs, or querying view specifications.
Install
mkdir -p .claude/skills/frontend-architecture-dieuxuanhien && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15196" && unzip -o skill.zip -d .claude/skills/frontend-architecture-dieuxuanhien && rm skill.zipInstalls to .claude/skills/frontend-architecture-dieuxuanhien
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Frontend architecture and design patterns for SeShop. Use when: understanding React component structure, designing state management, integrating APIs, or querying view specifications.183 chars✓ has a “when” trigger
About this skill
SeShop Frontend Architecture Skill
This skill provides architectural knowledge for SeShop frontend implementation using React, TypeScript, and Vite.
When to Use
- Understanding component hierarchy and composition
- Designing state management patterns
- Integrating backend APIs with proper error handling
- Implementing role-based UI routing
- Optimizing component performance
Architecture Overview
Framework: React 18+ with TypeScript
Build Tool: Vite
State Management: Redux Toolkit or Context API
HTTP Client: Axios with interceptors
Routing: React Router v6
Directory Structure
src/
├── pages/ → Top-level pages (route destinations)
│ ├── Admin/
│ │ ├── Dashboard.tsx → ADMIN_001
│ │ ├── UserManagement.tsx → ADMIN_002
│ │ ├── LocationsManagement.tsx → ADMIN_003
│ │ └── ...
│ ├── Staff/
│ │ ├── CatalogManagement.tsx → STAFF_001
│ │ ├── InventoryAdjustment.tsx → STAFF_002
│ │ └── ...
│ └── Customer/
│ ├── Browse.tsx → CUST_001
│ ├── ProductDetail.tsx → CUST_002
│ └── ...
│
├── components/ → Reusable components
│ ├── common/
│ │ ├── Header.tsx
│ │ ├── Sidebar.tsx
│ │ ├── Footer.tsx
│ │ └── ErrorBoundary.tsx
│ ├── forms/
│ │ ├── LoginForm.tsx
│ │ ├── ProductForm.tsx
│ │ └── CheckoutForm.tsx
│ ├── tables/
│ │ ├── ProductsTable.tsx
│ │ ├── OrdersTable.tsx
│ │ └── InventoryTable.tsx
│ └── widgets/
│ ├── StockWidget.tsx
│ ├── PriceDisplay.tsx
│ └── AIRecommendationChat.tsx
│
├── hooks/ → Custom React hooks
│ ├── useAuth.ts → Auth state and methods
│ ├── useApi.ts → API calls with loading/error
│ ├── useLocalStorage.ts → Persist state
│ └── useDebounce.ts → Debounce values
│
├── store/ → Redux slices (if using Redux)
│ ├── authSlice.ts → User, permissions, token
│ ├── cartSlice.ts → Cart items, total
│ ├── orderSlice.ts → Current/recent orders
│ └── inventorySlice.ts → Stock levels
│
├── api/ → API client and endpoints
│ ├── client.ts → Axios instance with interceptors
│ ├── auth.ts → /auth/* endpoints
│ ├── products.ts → /products/* endpoints
│ ├── orders.ts → /orders/* endpoints
│ └── inventory.ts → /inventory/* endpoints
│
├── types/ → TypeScript interfaces
│ ├── models.ts → Domain models (User, Product, Order, etc.)
│ ├── api.ts → API request/response types
│ └── store.ts → Redux state types
│
├── utils/ → Helper functions
│ ├── validators.ts → Form validation
│ ├── formatters.ts → Currency, dates, etc.
│ ├── constants.ts → App constants
│ └── permissions.ts → Permission checking
│
├── styles/ → Global styles
│ ├── index.css → Global styles
│ ├── variables.css → CSS variables (colors, spacing)
│ └── breakpoints.ts → Responsive breakpoints
│
├── App.tsx → Root component with routing
└── main.tsx → Entry point
Component Design Patterns
1. Page Components (Route-Level)
// pages/Admin/Dashboard.tsx
import React, { useEffect, useState } from 'react';
import { useAuth } from '@/hooks/useAuth';
import { dashboardAPI } from '@/api/dashboard';
import { Header } from '@/components/common/Header';
import { ErrorAlert } from '@/components/common/ErrorAlert';
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
export const DashboardPage: React.FC = () => {
const { user } = useAuth();
const [metrics, setMetrics] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!user?.permissions.includes('DASHBOARD:VIEW')) {
setError('You do not have permission to view this page');
return;
}
dashboardAPI.getMetrics()
.then(setMetrics)
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, [user]);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorAlert message={error} />;
return (
<>
<Header />
<main className="dashboard">
{/* Dashboard content */}
</main>
</>
);
};
2. Reusable Components
// components/forms/ProductForm.tsx
interface ProductFormProps {
product?: Product;
onSubmit: (data: ProductFormData) => Promise<void>;
onCancel: () => void;
}
export const ProductForm: React.FC<ProductFormProps> = ({
product,
onSubmit,
onCancel,
}) => {
const [formData, setFormData] = useState<ProductFormData>(
product ? toFormData(product) : emptyFormData
);
const [errors, setErrors] = useState<FormErrors>({});
const [loading, setLoading] = useState(false);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setErrors({});
setLoading(true);
try {
const validationErrors = validateProductForm(formData);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
await onSubmit(formData);
} catch (err) {
setErrors({ submit: err.message });
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
{/* Form fields with error display */}
<button type="submit" disabled={loading}>
{loading ? 'Saving...' : 'Save'}
</button>
<button type="button" onClick={onCancel}>
Cancel
</button>
</form>
);
};
3. Custom Hooks for API Calls
// hooks/useApi.ts
export function useApi<T>(apiCall: () => Promise<T>) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let isMounted = true;
apiCall()
.then(result => {
if (isMounted) setData(result);
})
.catch(err => {
if (isMounted) setError(err.message);
})
.finally(() => {
if (isMounted) setLoading(false);
});
return () => {
isMounted = false;
};
}, []);
return { data, loading, error, refetch: apiCall };
}
// Usage in component
const { data: orders, loading, error } = useApi(() =>
ordersAPI.getMyOrders()
);
4. State Management (Redux)
// store/cartSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface CartItem {
skuId: number;
quantity: number;
price: number;
}
interface CartState {
items: CartItem[];
total: number;
}
const initialState: CartState = {
items: [],
total: 0,
};
const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
addItem: (state, action: PayloadAction<CartItem>) => {
const existing = state.items.find(i => i.skuId === action.payload.skuId);
if (existing) {
existing.quantity += action.payload.quantity;
} else {
state.items.push(action.payload);
}
state.total = state.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
},
removeItem: (state, action: PayloadAction<number>) => {
state.items = state.items.filter(i => i.skuId !== action.payload);
state.total = state.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
},
},
});
export const { addItem, removeItem } = cartSlice.actions;
export default cartSlice.reducer;
API Integration Pattern
// api/client.ts
import axios, { AxiosInstance } from 'axios';
import { useAuth } from '@/hooks/useAuth';
export const apiClient: AxiosInstance = axios.create({
baseURL: import.meta.env.VITE_API_URL,
timeout: 10000,
});
// Add token to every request
apiClient.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Handle errors globally
apiClient.interceptors.response.use(
response => response.data,
error => {
if (error.response?.status === 401) {
// Token expired, redirect to login
localStorage.removeItem('token');
window.location.href = '/login';
}
return Promise.reject(error.response?.data || error);
}
);
// api/products.ts
export const productsAPI = {
list: (params?: { page?: number; limit?: number }) =>
apiClient.get('/products', { params }),
getById: (id: number) =>
apiClient.get(`/products/${id}`),
create: (data: ProductFormData) =>
apiClient.post('/products', data),
update: (id: number, data: ProductFormData) =>
apiClient.put(`/products/${id}`, data),
};
Routing with Role-Based Access
// App.tsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { useAuth } from '@/hooks/useAuth';
import { LoginPage } from '@/pages/LoginPage';
import { DashboardPage } from '@/pages/Admin/Dashboard';
import { Unauthorized } from '@/pages/Unauthorized';
const ProtectedRoute: React.FC<{
component: React.ReactNode;
requiredPermission?: string;
}> = ({ component, requiredPermission }) => {
const { user } = useAuth();
if (!user) {
return <Navigate to="/login" />;
}
if (requiredPermission && !user.permissions.includes(requiredPermission)) {
return <Unauthorized />;
}
return component;
};
export function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route
path="/admin/dashboard"
element={
<ProtectedRoute
component={<DashboardPage />}
requiredPermission="DASHBOARD:VIEW"
/>
}
/>
{/* More routes */}
</Routes>
</BrowserRouter>
);
}
Performance Optimization
1. Code Splitting by Route
const Dashboard = lazy(() => import('@/pages/A
---
*Content truncated.*