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>
11 KiB
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
cd frontend
npm install
npx playwright install
2. Start Backend
# 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
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
# 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
# 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:
import { APPOINTMENTS, SERVICES } from '../mockData';
const Dashboard = () => {
const [appointments] = useState(APPOINTMENTS);
const [services] = useState(SERVICES);
// ...
}
After:
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:
import { CUSTOMERS } from '../mockData';
const Customers = () => {
const [customers] = useState(CUSTOMERS);
const [searchTerm, setSearchTerm] = useState('');
// ...
}
After:
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:
import { APPOINTMENTS, RESOURCES, SERVICES } from '../mockData';
const Scheduler = () => {
const [appointments] = useState(APPOINTMENTS);
const [resources] = useState(RESOURCES);
const [services] = useState(SERVICES);
// ...
}
After:
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:
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:
// 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
# 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/:
// 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:
- Check Tailwind classes are identical
- Verify dark mode is applied correctly
- Compare CSS files from extracted version
- Use Playwright UI mode to inspect elements
Issue: CORS errors in console
Symptom: "Access-Control-Allow-Origin" errors
Solution:
- Verify backend is running
- Check CORS settings in
backend/config/settings/base.py - Ensure
X-Business-Subdomainheader is in CORS_ALLOW_HEADERS
Issue: 404 on API calls
Symptom: API calls return 404
Solution:
- Check you're using correct subdomain:
acme.lvh.me:5173 - Verify backend middleware is processing
X-Business-Subdomainheader - Check business exists in database with correct subdomain
Issue: Data not loading
Symptom: Components show loading forever
Solution:
- Check browser console for errors
- Verify backend migrations are applied
- Create test data in backend
- Check React Query DevTools (install
@tanstack/react-query-devtools)
Adding React Query DevTools
For debugging:
npm install @tanstack/react-query-devtools
In App.tsx:
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
- Error Boundaries: Add error boundaries for better error handling
- Loading States: Improve loading UI with skeletons
- Optimistic Updates: Add optimistic updates for better UX
- Form Validation: Add form validation with react-hook-form or similar
- Toast Notifications: Add toast library for success/error messages
- Accessibility: Add ARIA labels and keyboard navigation
- Performance: Optimize re-renders with React.memo
- 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.