Files
smoothschedule/legacy_reference/frontend/README_INTEGRATION.md
poduck 2e111364a2 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>
2025-11-27 01:43:20 -05:00

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:

  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:

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

  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.