Initial commit: SmoothSchedule multi-tenant scheduling platform

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>
This commit is contained in:
poduck
2025-11-27 01:43:20 -05:00
commit 2e111364a2
567 changed files with 96410 additions and 0 deletions

View File

@@ -0,0 +1,423 @@
# 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.