# Architecture Documentation
Comprehensive guide to the Black Canyon Tickets React rebuild architecture, design patterns, and technical decisions.
## Project Structure Overview
```
src/
├── components/
│ ├── ui/ # Design system primitives
│ ├── layout/ # Application layout system
│ ├── auth/ # Authentication components
│ ├── loading/ # Loading states and skeletons
│ ├── errors/ # Error boundaries and fallbacks
│ ├── events/ # Event domain components
│ ├── tickets/ # Ticketing domain components
│ ├── checkout/ # Purchase flow components
│ ├── billing/ # Payment and fee components
│ └── scanning/ # QR scanning components
├── pages/ # Route-level components
├── contexts/ # React Context providers
├── hooks/ # Custom React hooks
├── types/ # TypeScript type definitions
├── design-tokens/ # Design system configuration
├── styles/ # CSS files and utilities
└── utils/ # Utility functions
```
## Architectural Principles
### 1. Component Composition
**Philosophy**: Build complex UIs by composing smaller, focused components rather than creating monolithic components.
```tsx
// Bad: Monolithic component
function EventPage({ eventId }) {
return (
);
}
// Good: Composed from smaller components
function EventPage({ eventId }) {
return (
);
}
```
### 2. Design Token System
**Philosophy**: Centralize design decisions in a token system that enables consistent theming and maintainable styles.
```
design-tokens/
├── base.json # Core design tokens
└── themes/
├── light.json # Light theme overrides
└── dark.json # Dark theme overrides
```
**Token Categories**:
- **Colors**: Semantic color system (primary, surface, text, border)
- **Typography**: Font sizes, line heights, font families
- **Spacing**: Consistent spacing scale (1-20)
- **Border Radius**: Corner radius values (sm, md, lg, xl, 2xl)
- **Shadows**: Elevation system with multiple levels
### 3. Type-Driven Development
**Philosophy**: Use TypeScript's type system to catch errors early and provide excellent developer experience.
```typescript
// Comprehensive type definitions
interface Event {
id: string;
title: string;
description: string;
date: string;
venue: string;
organization: Organization;
ticketTypes: TicketType[];
status: EventStatus;
}
// Union types for controlled values
type EventStatus = 'draft' | 'published' | 'active' | 'completed' | 'cancelled';
type UserRole = 'user' | 'admin' | 'super_admin';
// Strict component props
interface EventCardProps {
event: Event;
showActions?: boolean;
onEdit?: (event: Event) => void;
onDelete?: (eventId: string) => void;
}
```
## Design Patterns
### 1. Compound Components
**Use Case**: Complex components with multiple related parts that work together.
```tsx
// Card component with sub-components
function Card({ children, variant = 'default', ...props }) {
return (
{children}
);
}
Card.Header = function CardHeader({ children, className = '', ...props }) {
return (
{children}
);
};
Card.Content = function CardContent({ children, className = '', ...props }) {
return (
{children}
);
};
// Usage
Event Details
```
### 2. Render Props Pattern
**Use Case**: Sharing stateful logic between components while maintaining flexibility in rendering.
```tsx
// ProtectedRoute component using render props
function ProtectedRoute({
permission,
fallback,
children
}: ProtectedRouteProps) {
const { user, hasPermission } = useAuth();
if (!user) {
return ;
}
if (permission && !hasPermission(permission)) {
return fallback || ;
}
return <>{children}>;
}
// Usage
}
>
```
### 3. Custom Hook Pattern
**Use Case**: Extracting and reusing stateful logic across components.
```tsx
// useAuth hook encapsulates authentication logic
function useAuth() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const login = useCallback(async (credentials: LoginCredentials) => {
setLoading(true);
try {
const user = await authService.login(credentials);
setUser(user);
return { success: true, user };
} catch (error) {
return { success: false, error: error.message };
} finally {
setLoading(false);
}
}, []);
const logout = useCallback(() => {
setUser(null);
authService.logout();
}, []);
const hasPermission = useCallback((permission: Permission) => {
return user?.permissions.includes(permission) ?? false;
}, [user]);
return {
user,
loading,
login,
logout,
hasPermission,
isAuthenticated: !!user,
};
}
```
## State Management Strategy
### 1. Component State (useState)
**Use For**: Local component state that doesn't need to be shared.
```tsx
function TicketSelector({ ticketType }) {
const [quantity, setQuantity] = useState(0);
const [loading, setLoading] = useState(false);
return (
);
}
```
### 2. Context State (React Context)
**Use For**: Application-wide state that needs to be shared across many components.
```tsx
// Theme context for global theme management
const ThemeContext = createContext(undefined);
function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState('dark');
const toggleTheme = useCallback(() => {
setTheme(prev => prev === 'light' ? 'dark' : 'light');
}, []);
return (
{children}
);
}
// Auth context for user authentication state
const AuthContext = createContext(undefined);
function AuthProvider({ children }: { children: React.ReactNode }) {
const auth = useAuth(); // Custom hook with auth logic
return (
{children}
);
}
```
### 3. URL State (React Router)
**Use For**: State that should be reflected in the URL for bookmarking and sharing.
```tsx
// Search and filter state in URL parameters
function EventsPage() {
const [searchParams, setSearchParams] = useSearchParams();
const search = searchParams.get('search') || '';
const category = searchParams.get('category') || 'all';
const updateSearch = (newSearch: string) => {
setSearchParams(prev => {
prev.set('search', newSearch);
return prev;
});
};
return (
);
}
```
## Error Handling Architecture
### 1. Error Boundaries
**Strategy**: Catch React component errors and provide graceful fallbacks.
```tsx
// App-level error boundary
class AppErrorBoundary extends Component {
constructor(props: ErrorBoundaryProps) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { hasError: true, error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
// Report to error tracking service
}
render() {
if (this.state.hasError) {
return this.props.fallback || ;
}
return this.props.children;
}
}
// Usage
}>
```
### 2. Loading States
**Strategy**: Provide consistent loading experiences across the application.
```tsx
// Suspense for route-level loading
function App() {
return (
}>
} />
);
}
// Component-level loading with Skeleton
function EventCard({ eventId }: { eventId: string }) {
const { event, loading, error } = useEvent(eventId);
if (loading) return ;
if (error) return ;
if (!event) return ;
return ;
}
```
## Authentication Architecture
### 1. Mock Authentication System
**Design**: Simulates real authentication without external dependencies.
```typescript
// Mock auth service
class MockAuthService {
private static users: User[] = [
{
id: '1',
email: 'demo@blackcanyontickets.com',
role: 'user',
permissions: ['events:read', 'tickets:purchase']
},
{
id: '2',
email: 'admin@blackcanyontickets.com',
role: 'admin',
permissions: ['events:read', 'events:write', 'users:read']
}
];
async login(credentials: LoginCredentials): Promise {
const user = this.users.find(u => u.email === credentials.email);
if (!user) throw new Error('Invalid credentials');
// Simulate API delay
await new Promise(resolve => setTimeout(resolve, 1000));
// Store in localStorage for persistence
localStorage.setItem('auth_user', JSON.stringify(user));
return user;
}
logout(): void {
localStorage.removeItem('auth_user');
}
getCurrentUser(): User | null {
const stored = localStorage.getItem('auth_user');
return stored ? JSON.parse(stored) : null;
}
}
```
### 2. Permission System
**Design**: Role-based access control with granular permissions.
```typescript
// Permission definitions
type Permission =
| 'events:read' | 'events:write' | 'events:delete'
| 'tickets:read' | 'tickets:purchase' | 'tickets:scan'
| 'users:read' | 'users:write'
| 'analytics:read' | 'settings:write';
// Role definitions
const ROLE_PERMISSIONS: Record = {
user: [
'events:read',
'tickets:read',
'tickets:purchase'
],
admin: [
'events:read', 'events:write',
'tickets:read', 'tickets:scan',
'users:read',
'analytics:read'
],
super_admin: [
'events:read', 'events:write', 'events:delete',
'tickets:read', 'tickets:scan',
'users:read', 'users:write',
'analytics:read',
'settings:write'
]
};
```
## Component Testing Strategy
### 1. Unit Testing
**Focus**: Individual component behavior and props handling.
```typescript
// Button component tests
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from '@/components/ui/Button';
describe('Button Component', () => {
test('renders with correct variant styles', () => {
render();
const button = screen.getByRole('button');
expect(button).toHaveClass('bg-primary');
});
test('handles click events', () => {
const handleClick = jest.fn();
render();
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
test('displays loading state', () => {
render();
expect(screen.getByRole('button')).toBeDisabled();
expect(screen.getByText('Loading')).toBeInTheDocument();
});
});
```
### 2. Integration Testing with Playwright
**Focus**: End-to-end user workflows and cross-component interactions.
```typescript
// Authentication flow test
import { test, expect } from '@playwright/test';
test('user can log in and access dashboard', async ({ page }) => {
await page.goto('/login');
// Fill login form
await page.fill('[data-testid="email-input"]', 'demo@blackcanyontickets.com');
await page.fill('[data-testid="password-input"]', 'demo123');
// Submit and verify redirect
await page.click('[data-testid="login-button"]');
await expect(page).toHaveURL('/dashboard');
// Verify user is authenticated
await expect(page.getByText('Welcome back')).toBeVisible();
});
```
### 3. Visual Regression Testing
**Focus**: Ensure UI changes don't break visual design.
```typescript
// Visual tests with Playwright
test('homepage renders correctly', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png');
});
test('login form in both themes', async ({ page }) => {
// Test light theme
await page.goto('/login');
await page.getByTestId('theme-toggle').click(); // Switch to light
await expect(page.getByTestId('login-form')).toHaveScreenshot('login-light.png');
// Test dark theme
await page.getByTestId('theme-toggle').click(); // Switch to dark
await expect(page.getByTestId('login-form')).toHaveScreenshot('login-dark.png');
});
```
## Performance Architecture
### 1. Code Splitting
**Strategy**: Split code at route boundaries and for large dependencies.
```tsx
// Route-based code splitting
const HomePage = lazy(() => import('@/pages/HomePage'));
const DashboardPage = lazy(() => import('@/pages/DashboardPage'));
const EventsPage = lazy(() => import('@/pages/EventsPage'));
function App() {
return (
}>
} />
}>
} />
);
}
```
### 2. Component Optimization
**Strategy**: Use React.memo and useMemo to prevent unnecessary re-renders.
```tsx
// Memoized component to prevent re-renders
const EventCard = memo(function EventCard({ event, onEdit }: EventCardProps) {
const formattedDate = useMemo(() => {
return formatDate(event.date);
}, [event.date]);
return (
{event.title}
{formattedDate}
);
});
// Optimized list rendering
function EventList({ events }: { events: Event[] }) {
const sortedEvents = useMemo(() => {
return [...events].sort((a, b) =>
new Date(a.date).getTime() - new Date(b.date).getTime()
);
}, [events]);
return (
{sortedEvents.map(event => (
))}
);
}
```
## Accessibility Architecture
### 1. Semantic HTML Foundation
**Strategy**: Use semantic HTML elements that provide built-in accessibility.
```tsx
// Good: Semantic structure
function EventForm() {
return (
);
}
```
### 2. ARIA Enhancement
**Strategy**: Enhance semantic HTML with ARIA attributes where needed.
```tsx
// Complex component with ARIA
function Select({ options, value, onChange, label }: SelectProps) {
const [isOpen, setIsOpen] = useState(false);
const [focusedIndex, setFocusedIndex] = useState(-1);
return (
{isOpen && (
{options.map((option, index) => (
- onChange(option.value)}
>
{option.label}
))}
)}
);
}
```
## Build and Deployment Architecture
### 1. Vite Configuration
**Strategy**: Optimize builds for production with proper chunk splitting.
```typescript
// vite.config.ts
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
ui: ['lucide-react']
}
}
}
},
css: {
postcss: {
plugins: [tailwindcss, autoprefixer]
}
}
});
```
### 2. Environment Configuration
**Strategy**: Support multiple environments with appropriate configurations.
```typescript
// Environment-specific configuration
const config = {
development: {
apiUrl: 'http://localhost:3001',
enableDevTools: true,
logLevel: 'debug'
},
production: {
apiUrl: 'https://api.blackcanyontickets.com',
enableDevTools: false,
logLevel: 'error'
}
};
export default config[process.env.NODE_ENV || 'development'];
```
---
**Architecture designed with CrispyGoat principles - scalable, maintainable, and developer-friendly.**