This commit includes: - Django backend with multi-tenancy (django-tenants) - React + TypeScript frontend with Vite - Platform administration API with role-based access control - Authentication system with token-based auth - Quick login dev tools for testing different user roles - CORS and CSRF configuration for local development - Docker development environment setup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
424 lines
11 KiB
Markdown
424 lines
11 KiB
Markdown
# Frontend Integration - Step by Step
|
|
|
|
## Current Status
|
|
|
|
✅ **API Infrastructure Complete**
|
|
- API client with axios interceptors
|
|
- Authentication hooks
|
|
- All feature hooks (customers, services, resources, appointments, business)
|
|
- Playwright testing setup
|
|
- Login page component
|
|
- Example App.tsx with real auth
|
|
|
|
✅ **Components Extracted**
|
|
- All components from zip copied to `src/*-extracted/` directories
|
|
- TypeScript types copied to `src/types.ts`
|
|
|
|
## Quick Start (5 Minutes)
|
|
|
|
### 1. Install Dependencies
|
|
```bash
|
|
cd frontend
|
|
npm install
|
|
npx playwright install
|
|
```
|
|
|
|
### 2. Start Backend
|
|
```bash
|
|
# In another terminal
|
|
cd backend
|
|
docker-compose up
|
|
docker-compose exec backend python manage.py migrate
|
|
docker-compose exec backend python manage.py createsuperuser
|
|
```
|
|
|
|
### 3. Start Frontend
|
|
```bash
|
|
npm run dev
|
|
```
|
|
|
|
### 4. Test Login
|
|
- Go to `http://lvh.me:5173`
|
|
- Login with your superuser credentials
|
|
- You should see a welcome screen with your user info!
|
|
|
|
## Integration Steps
|
|
|
|
### Step 1: Replace App.tsx
|
|
|
|
```bash
|
|
# Backup current App.tsx
|
|
mv src/App.tsx src/App.tsx.backup
|
|
|
|
# Use the integrated version
|
|
mv src/App-integrated.tsx src/App.tsx
|
|
```
|
|
|
|
### Step 2: Copy Components
|
|
|
|
```bash
|
|
# Copy all components at once
|
|
cp -r src/components-extracted/* src/components/ 2>/dev/null || true
|
|
cp -r src/layouts-extracted/* src/layouts/ 2>/dev/null || true
|
|
cp -r src/pages-extracted/* src/pages/ 2>/dev/null || true
|
|
|
|
# Or copy selectively, one feature at a time (recommended)
|
|
```
|
|
|
|
### Step 3: Update Component Imports
|
|
|
|
For each component you copy, replace mock data with API hooks.
|
|
|
|
#### Example: Dashboard.tsx
|
|
|
|
**Before:**
|
|
```typescript
|
|
import { APPOINTMENTS, SERVICES } from '../mockData';
|
|
|
|
const Dashboard = () => {
|
|
const [appointments] = useState(APPOINTMENTS);
|
|
const [services] = useState(SERVICES);
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```typescript
|
|
import { useAppointments } from '../hooks/useAppointments';
|
|
import { useServices } from '../hooks/useServices';
|
|
|
|
const Dashboard = () => {
|
|
const { data: appointments, isLoading: apptLoading } = useAppointments();
|
|
const { data: services, isLoading: servicesLoading } = useServices();
|
|
|
|
if (apptLoading || servicesLoading) {
|
|
return <div>Loading...</div>;
|
|
}
|
|
// ...
|
|
}
|
|
```
|
|
|
|
#### Example: Customers.tsx
|
|
|
|
**Before:**
|
|
```typescript
|
|
import { CUSTOMERS } from '../mockData';
|
|
|
|
const Customers = () => {
|
|
const [customers] = useState(CUSTOMERS);
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```typescript
|
|
import { useCustomers } from '../hooks/useCustomers';
|
|
|
|
const Customers = () => {
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const { data: customers, isLoading } = useCustomers({ search: searchTerm });
|
|
|
|
if (isLoading) {
|
|
return <div>Loading...</div>;
|
|
}
|
|
// ...
|
|
}
|
|
```
|
|
|
|
#### Example: Scheduler.tsx
|
|
|
|
**Before:**
|
|
```typescript
|
|
import { APPOINTMENTS, RESOURCES, SERVICES } from '../mockData';
|
|
|
|
const Scheduler = () => {
|
|
const [appointments] = useState(APPOINTMENTS);
|
|
const [resources] = useState(RESOURCES);
|
|
const [services] = useState(SERVICES);
|
|
// ...
|
|
}
|
|
```
|
|
|
|
**After:**
|
|
```typescript
|
|
import { useAppointments } from '../hooks/useAppointments';
|
|
import { useResources } from '../hooks/useResources';
|
|
import { useServices } from '../hooks/useServices';
|
|
|
|
const Scheduler = () => {
|
|
const { data: appointments, isLoading: apptLoading } = useAppointments();
|
|
const { data: resources, isLoading: resLoading } = useResources();
|
|
const { data: services, isLoading: servicesLoading } = useServices();
|
|
|
|
if (apptLoading || resLoading || servicesLoading) {
|
|
return <div>Loading...</div>;
|
|
}
|
|
// ...
|
|
}
|
|
```
|
|
|
|
### Step 4: Handle Create/Update/Delete Operations
|
|
|
|
For mutations, use the mutation hooks:
|
|
|
|
```typescript
|
|
import { useCreateCustomer, useUpdateCustomer, useDeleteCustomer } from '../hooks/useCustomers';
|
|
|
|
const Customers = () => {
|
|
const { data: customers } = useCustomers();
|
|
const createMutation = useCreateCustomer();
|
|
const updateMutation = useUpdateCustomer();
|
|
const deleteMutation = useDeleteCustomer();
|
|
|
|
const handleCreateCustomer = (data: any) => {
|
|
createMutation.mutate(data, {
|
|
onSuccess: () => {
|
|
console.log('Customer created!');
|
|
// Maybe close a modal or reset a form
|
|
},
|
|
onError: (error) => {
|
|
console.error('Failed to create customer:', error);
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleUpdateCustomer = (id: string, updates: any) => {
|
|
updateMutation.mutate({ id, updates }, {
|
|
onSuccess: () => {
|
|
console.log('Customer updated!');
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleDeleteCustomer = (id: string) => {
|
|
deleteMutation.mutate(id, {
|
|
onSuccess: () => {
|
|
console.log('Customer deleted!');
|
|
}
|
|
});
|
|
};
|
|
|
|
return (
|
|
// Your JSX with handlers
|
|
<button onClick={() => handleCreateCustomer(newCustomerData)}>
|
|
Create Customer
|
|
</button>
|
|
);
|
|
};
|
|
```
|
|
|
|
### Step 5: Update App.tsx with Full Routing
|
|
|
|
Once components are copied and updated, implement role-based routing in App.tsx:
|
|
|
|
```typescript
|
|
// Import all your pages
|
|
import Dashboard from './pages/Dashboard';
|
|
import Scheduler from './pages/Scheduler';
|
|
import Customers from './pages/Customers';
|
|
// ... etc
|
|
|
|
const AppContent = () => {
|
|
const { data: user } = useCurrentUser();
|
|
const { data: business } = useCurrentBusiness();
|
|
|
|
if (!user) return <LoginPage />;
|
|
|
|
// Platform users
|
|
if (['superuser', 'platform_manager', 'platform_support'].includes(user.role)) {
|
|
return (
|
|
<Routes>
|
|
<Route element={<PlatformLayout user={user} />}>
|
|
<Route path="/platform/dashboard" element={<PlatformDashboard />} />
|
|
<Route path="/platform/businesses" element={<PlatformBusinesses />} />
|
|
{/* ... more platform routes */}
|
|
</Route>
|
|
</Routes>
|
|
);
|
|
}
|
|
|
|
// Customer users
|
|
if (user.role === 'customer') {
|
|
return (
|
|
<Routes>
|
|
<Route element={<CustomerLayout business={business} user={user} />}>
|
|
<Route path="/" element={<CustomerDashboard />} />
|
|
<Route path="/book" element={<BookingPage />} />
|
|
{/* ... more customer routes */}
|
|
</Route>
|
|
</Routes>
|
|
);
|
|
}
|
|
|
|
// Business users (owner, manager, staff, resource)
|
|
return (
|
|
<Routes>
|
|
<Route element={<BusinessLayout business={business} user={user} />}>
|
|
<Route path="/" element={<Dashboard />} />
|
|
<Route path="/scheduler" element={<Scheduler />} />
|
|
<Route path="/customers" element={<Customers />} />
|
|
<Route path="/resources" element={<Resources />} />
|
|
{/* ... more business routes */}
|
|
</Route>
|
|
</Routes>
|
|
);
|
|
};
|
|
```
|
|
|
|
## Testing with Playwright
|
|
|
|
### Run Visual Tests
|
|
|
|
```bash
|
|
# Run tests and compare with extracted frontend
|
|
npm run test:headed
|
|
|
|
# Or use UI mode for debugging
|
|
npm run test:ui
|
|
```
|
|
|
|
### Create New Tests
|
|
|
|
Create test files in `tests/e2e/`:
|
|
|
|
```typescript
|
|
// tests/e2e/customers.spec.ts
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Customers Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Login
|
|
await page.goto('/');
|
|
await page.getByLabel(/username/i).fill('testowner');
|
|
await page.getByLabel(/password/i).fill('testpass123');
|
|
await page.getByRole('button', { name: /sign in/i }).click();
|
|
await page.waitForURL(/\//);
|
|
});
|
|
|
|
test('should display customers list', async ({ page }) => {
|
|
await page.goto('/customers');
|
|
|
|
// Wait for customers to load
|
|
await page.waitForSelector('table tbody tr, [data-testid="customer-list"]');
|
|
|
|
// Take screenshot for comparison
|
|
await expect(page).toHaveScreenshot('customers-list.png');
|
|
});
|
|
|
|
test('should filter customers by status', async ({ page }) => {
|
|
await page.goto('/customers');
|
|
|
|
// Click status filter
|
|
await page.getByRole('button', { name: /filter/i }).click();
|
|
await page.getByRole('option', { name: /active/i }).click();
|
|
|
|
// Verify filtered results
|
|
const rows = await page.locator('table tbody tr').count();
|
|
expect(rows).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Issue: Types don't match
|
|
**Symptom**: TypeScript errors about id being string vs number
|
|
|
|
**Solution**: The hooks already handle transformation. Make sure you're using the hooks, not calling apiClient directly.
|
|
|
|
### Issue: Components look different from extracted version
|
|
**Symptom**: Visual differences in Playwright screenshots
|
|
|
|
**Solution**:
|
|
1. Check Tailwind classes are identical
|
|
2. Verify dark mode is applied correctly
|
|
3. Compare CSS files from extracted version
|
|
4. Use Playwright UI mode to inspect elements
|
|
|
|
### Issue: CORS errors in console
|
|
**Symptom**: "Access-Control-Allow-Origin" errors
|
|
|
|
**Solution**:
|
|
1. Verify backend is running
|
|
2. Check CORS settings in `backend/config/settings/base.py`
|
|
3. Ensure `X-Business-Subdomain` header is in CORS_ALLOW_HEADERS
|
|
|
|
### Issue: 404 on API calls
|
|
**Symptom**: API calls return 404
|
|
|
|
**Solution**:
|
|
1. Check you're using correct subdomain: `acme.lvh.me:5173`
|
|
2. Verify backend middleware is processing `X-Business-Subdomain` header
|
|
3. Check business exists in database with correct subdomain
|
|
|
|
### Issue: Data not loading
|
|
**Symptom**: Components show loading forever
|
|
|
|
**Solution**:
|
|
1. Check browser console for errors
|
|
2. Verify backend migrations are applied
|
|
3. Create test data in backend
|
|
4. Check React Query DevTools (install `@tanstack/react-query-devtools`)
|
|
|
|
## Adding React Query DevTools
|
|
|
|
For debugging:
|
|
|
|
```bash
|
|
npm install @tanstack/react-query-devtools
|
|
```
|
|
|
|
In App.tsx:
|
|
```typescript
|
|
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
|
|
|
<QueryClientProvider client={queryClient}>
|
|
<Router>
|
|
<AppContent />
|
|
</Router>
|
|
<ReactQueryDevtools initialIsOpen={false} />
|
|
</QueryClientProvider>
|
|
```
|
|
|
|
## Component Integration Checklist
|
|
|
|
- [ ] Login page works
|
|
- [ ] Dashboard displays
|
|
- [ ] Sidebar navigation works
|
|
- [ ] Dark mode toggle works
|
|
- [ ] Customers page loads data from API
|
|
- [ ] Can create new customer
|
|
- [ ] Can edit customer
|
|
- [ ] Can delete customer
|
|
- [ ] Services page loads data
|
|
- [ ] Resources page loads data
|
|
- [ ] Scheduler displays appointments
|
|
- [ ] Can create appointment
|
|
- [ ] Can drag-and-drop appointments
|
|
- [ ] Settings page loads business data
|
|
- [ ] Can update business settings
|
|
- [ ] Visual tests pass
|
|
- [ ] No console errors
|
|
|
|
## Next Steps After Basic Integration
|
|
|
|
1. **Error Boundaries**: Add error boundaries for better error handling
|
|
2. **Loading States**: Improve loading UI with skeletons
|
|
3. **Optimistic Updates**: Add optimistic updates for better UX
|
|
4. **Form Validation**: Add form validation with react-hook-form or similar
|
|
5. **Toast Notifications**: Add toast library for success/error messages
|
|
6. **Accessibility**: Add ARIA labels and keyboard navigation
|
|
7. **Performance**: Optimize re-renders with React.memo
|
|
8. **Tests**: Add more comprehensive Playwright tests
|
|
|
|
## Resources
|
|
|
|
- **Main Guide**: `../FRONTEND_INTEGRATION_GUIDE.md`
|
|
- **Backend API**: `../BACKEND_IMPLEMENTATION_SUMMARY.md`
|
|
- **All Hooks**: Check `src/hooks/` directory
|
|
- **Type Definitions**: `src/types.ts`
|
|
- **Example Components**: `src/*-extracted/` directories
|
|
|
|
You're ready to integrate! Start with the Dashboard, then move to individual features one by one.
|