295 lines
6.0 KiB
Markdown
295 lines
6.0 KiB
Markdown
# Development Guide
|
|
|
|
Complete guide for developing the Yaltopia Ticket Admin application.
|
|
|
|
## Tech Stack
|
|
|
|
- **Frontend**: React 18 + TypeScript + Vite
|
|
- **UI**: TailwindCSS + shadcn/ui
|
|
- **State**: React Query (TanStack Query)
|
|
- **Routing**: React Router v6
|
|
- **HTTP Client**: Axios
|
|
- **Forms**: React Hook Form + Zod
|
|
- **Charts**: Recharts
|
|
- **Notifications**: Sonner
|
|
- **Error Tracking**: Sentry
|
|
- **Testing**: Vitest + Testing Library
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# Set up environment
|
|
cp .env.example .env
|
|
# Edit .env with your backend URL
|
|
|
|
# Start development server
|
|
npm run dev
|
|
|
|
# Run tests
|
|
npm run test
|
|
|
|
# Build for production
|
|
npm run build
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
src/
|
|
├── components/ # Reusable UI components
|
|
├── pages/ # Page components
|
|
├── services/ # API service layer
|
|
│ ├── api/
|
|
│ │ └── client.ts # Axios instance
|
|
│ ├── auth.service.ts
|
|
│ ├── user.service.ts
|
|
│ └── ...
|
|
├── layouts/ # Layout components
|
|
├── lib/ # Utilities
|
|
└── test/ # Test utilities
|
|
```
|
|
|
|
## API Architecture
|
|
|
|
### Service Layer Pattern
|
|
|
|
All API calls go through typed service classes:
|
|
|
|
```
|
|
Component → Service → API Client → Backend
|
|
```
|
|
|
|
### Available Services
|
|
|
|
```typescript
|
|
import {
|
|
authService, // Authentication
|
|
userService, // User management
|
|
analyticsService, // Analytics
|
|
securityService, // Security
|
|
systemService, // System health
|
|
announcementService,// Announcements
|
|
auditService, // Audit logs
|
|
settingsService // Settings
|
|
} from '@/services'
|
|
```
|
|
|
|
### Usage Examples
|
|
|
|
**Fetching Data:**
|
|
```typescript
|
|
import { useQuery } from '@tanstack/react-query'
|
|
import { userService } from '@/services'
|
|
|
|
const { data, isLoading } = useQuery({
|
|
queryKey: ['users'],
|
|
queryFn: () => userService.getUsers({ page: 1, limit: 20 })
|
|
})
|
|
```
|
|
|
|
**Mutations:**
|
|
```typescript
|
|
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
import { userService } from '@/services'
|
|
|
|
const queryClient = useQueryClient()
|
|
const mutation = useMutation({
|
|
mutationFn: (id: string) => userService.deleteUser(id),
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['users'] })
|
|
toast.success('User deleted')
|
|
}
|
|
})
|
|
```
|
|
|
|
**Direct Calls:**
|
|
```typescript
|
|
import { authService } from '@/services'
|
|
|
|
const response = await authService.login({ email, password })
|
|
```
|
|
|
|
## Authentication
|
|
|
|
### Setup
|
|
|
|
1. Backend must return tokens on login
|
|
2. Frontend stores in httpOnly cookies (recommended) or localStorage
|
|
3. All requests automatically include auth token
|
|
4. 401 errors trigger automatic token refresh
|
|
|
|
### Login Flow
|
|
|
|
```typescript
|
|
// User logs in
|
|
const response = await authService.login({ email, password })
|
|
|
|
// Token stored automatically
|
|
// User redirected to dashboard
|
|
|
|
// All subsequent requests include token
|
|
await userService.getUsers() // Token added automatically
|
|
```
|
|
|
|
### Protected Routes
|
|
|
|
```typescript
|
|
<Route element={<ProtectedRoute />}>
|
|
<Route path="/admin/*" element={<AdminLayout />} />
|
|
</Route>
|
|
```
|
|
|
|
### Logout
|
|
|
|
```typescript
|
|
await authService.logout() // Clears tokens & cookies
|
|
navigate('/login')
|
|
```
|
|
|
|
## API Standards
|
|
|
|
### Service Methods
|
|
|
|
All service methods:
|
|
- Return typed data (no `response.data` unwrapping needed)
|
|
- Throw errors with `error.response.data.message`
|
|
- Use consistent naming (get, create, update, delete)
|
|
|
|
### Error Handling
|
|
|
|
```typescript
|
|
try {
|
|
await userService.deleteUser(id)
|
|
toast.success('User deleted')
|
|
} catch (error: any) {
|
|
toast.error(error.response?.data?.message || 'Operation failed')
|
|
}
|
|
```
|
|
|
|
### Type Safety
|
|
|
|
```typescript
|
|
// All responses are typed
|
|
const users: PaginatedResponse<User> = await userService.getUsers()
|
|
const stats: OverviewStats = await analyticsService.getOverview()
|
|
```
|
|
|
|
## Environment Variables
|
|
|
|
```bash
|
|
# Required
|
|
VITE_BACKEND_API_URL=http://localhost:3001/api/v1
|
|
|
|
# Optional (Sentry)
|
|
VITE_SENTRY_DSN=your-sentry-dsn
|
|
VITE_SENTRY_ENVIRONMENT=development
|
|
```
|
|
|
|
## Common Tasks
|
|
|
|
### Adding a New Service Method
|
|
|
|
```typescript
|
|
// src/services/user.service.ts
|
|
async exportUserData(userId: string): Promise<Blob> {
|
|
const response = await apiClient.get(`/admin/users/${userId}/export`, {
|
|
responseType: 'blob'
|
|
})
|
|
return response.data
|
|
}
|
|
```
|
|
|
|
### Adding a New Page
|
|
|
|
1. Create page component in `src/pages/`
|
|
2. Add route in `src/App.tsx`
|
|
3. Import required services
|
|
4. Use React Query for data fetching
|
|
|
|
### Adding a New Component
|
|
|
|
1. Create in `src/components/`
|
|
2. Use TypeScript for props
|
|
3. Follow existing patterns
|
|
4. Add to component exports if reusable
|
|
|
|
## Best Practices
|
|
|
|
### React Query
|
|
|
|
```typescript
|
|
// Good - specific query keys
|
|
queryKey: ['users', page, limit, search]
|
|
|
|
// Bad - too generic
|
|
queryKey: ['data']
|
|
```
|
|
|
|
### Service Layer
|
|
|
|
```typescript
|
|
// Good - use services
|
|
import { userService } from '@/services'
|
|
await userService.getUsers()
|
|
|
|
// Bad - direct axios
|
|
import axios from 'axios'
|
|
await axios.get('/api/users')
|
|
```
|
|
|
|
### Error Handling
|
|
|
|
```typescript
|
|
// Good - handle errors
|
|
try {
|
|
await userService.deleteUser(id)
|
|
} catch (error: any) {
|
|
toast.error(error.response?.data?.message)
|
|
}
|
|
|
|
// Bad - no error handling
|
|
await userService.deleteUser(id)
|
|
```
|
|
|
|
### Type Safety
|
|
|
|
```typescript
|
|
// Good - use types
|
|
const users: PaginatedResponse<User> = await userService.getUsers()
|
|
|
|
// Bad - any type
|
|
const users: any = await userService.getUsers()
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### CORS Errors
|
|
- Ensure backend has CORS configured
|
|
- Check `withCredentials: true` in API client
|
|
- Verify `VITE_BACKEND_API_URL` is correct
|
|
|
|
### 401 Errors
|
|
- Check token is being sent
|
|
- Verify backend token validation
|
|
- Check token expiration
|
|
|
|
### Build Errors
|
|
- Run `npm run build` to check TypeScript errors
|
|
- Fix any type errors
|
|
- Ensure all imports are correct
|
|
|
|
### Test Failures
|
|
- Run `npm run test` to see failures
|
|
- Check mock implementations
|
|
- Verify test data matches types
|
|
|
|
## Additional Resources
|
|
|
|
- [Testing Guide](./TESTING.md)
|
|
- [Deployment Guide](./DEPLOYMENT.md)
|
|
- [Security Guide](./SECURITY.md)
|
|
- [Troubleshooting](./TROUBLESHOOTING.md)
|