# SmoothSchedule Frontend Development Guide

## Project Overview

This is the React frontend for SmoothSchedule, a multi-tenant scheduling platform.

**See also:** `/home/poduck/Desktop/smoothschedule2/smoothschedule/CLAUDE.md` for backend documentation.

## Key Paths

```
/home/poduck/Desktop/smoothschedule2/
├── frontend/                         # This React frontend
│   ├── src/
│   │   ├── api/client.ts             # Axios API client
│   │   ├── components/               # Feature components
│   │   │   └── ui/                   # Reusable UI components (see below)
│   │   ├── constants/                # Shared constants
│   │   │   └── schedulePresets.ts    # Schedule/cron presets
│   │   ├── hooks/                    # React Query hooks (useResources, useAuth, etc.)
│   │   ├── pages/                    # Page components
│   │   ├── types.ts                  # TypeScript interfaces
│   │   ├── i18n/locales/en.json      # English translations
│   │   └── utils/cookies.ts          # Cookie utilities
│   ├── .env.development              # Frontend env vars
│   └── vite.config.ts                # Vite configuration
│
└── smoothschedule/                   # Django backend (runs in Docker!)
    ├── docker-compose.local.yml      # Docker config
    ├── .envs/.local/                 # Backend env vars
    ├── config/settings/              # Django settings
    └── smoothschedule/
        ├── schedule/                 # Core scheduling app
        └── users/                    # User management
```

## Reusable UI Components

All reusable UI components are in `src/components/ui/`. Import from the barrel file:

```typescript
import { Modal, FormInput, Button, Alert } from '../components/ui';
```

### Available Components

| Component | Description |
|-----------|-------------|
| **Modal** | Reusable modal dialog with header, body, footer |
| **ModalFooter** | Standardized modal footer with buttons |
| **FormInput** | Text input with label, error, hint support |
| **FormSelect** | Select dropdown with label, error support |
| **FormTextarea** | Textarea with label, error support |
| **FormCurrencyInput** | ATM-style currency input (cents) |
| **CurrencyInput** | Raw currency input component |
| **Button** | Button with variants, loading state, icons |
| **SubmitButton** | Pre-configured submit button |
| **Alert** | Alert banner (error, success, warning, info) |
| **ErrorMessage** | Error alert shorthand |
| **SuccessMessage** | Success alert shorthand |
| **TabGroup** | Tab navigation (default, pills, underline) |
| **StepIndicator** | Multi-step wizard indicator |
| **LoadingSpinner** | Loading spinner with variants |
| **PageLoading** | Full page loading state |
| **Card** | Card container with header/body/footer |
| **EmptyState** | Empty state placeholder |
| **Badge** | Status badges |

### Usage Examples

```typescript
// Modal with form
<Modal isOpen={isOpen} onClose={onClose} title="Edit Resource" size="lg">
  <FormInput
    label="Name"
    value={name}
    onChange={(e) => setName(e.target.value)}
    error={errors.name}
    required
  />
  <FormSelect
    label="Type"
    value={type}
    onChange={(e) => setType(e.target.value)}
    options={[
      { value: 'STAFF', label: 'Staff' },
      { value: 'ROOM', label: 'Room' },
    ]}
  />
</Modal>

// Alert messages
{error && <ErrorMessage message={error} />}
{success && <SuccessMessage message="Saved successfully!" />}

// Tabs
<TabGroup
  tabs={[
    { id: 'details', label: 'Details' },
    { id: 'schedule', label: 'Schedule' },
  ]}
  activeTab={activeTab}
  onChange={setActiveTab}
/>
```

## Utility Hooks

### useCrudMutation

Factory hook for CRUD mutations with React Query:

```typescript
import { useCrudMutation, createCrudHooks } from '../hooks/useCrudMutation';

// Simple usage
const createResource = useCrudMutation<Resource, CreateResourceData>({
  endpoint: '/resources',
  method: 'POST',
  invalidateKeys: [['resources']],
});

// Create all CRUD hooks at once
const { useCreate, useUpdate, useDelete } = createCrudHooks<Resource>('/resources', 'resources');
```

### useFormValidation

Schema-based form validation:

```typescript
import { useFormValidation, required, email, minLength } from '../hooks/useFormValidation';

const schema = {
  email: [required('Email is required'), email('Invalid email')],
  password: [required(), minLength(8, 'Min 8 characters')],
};

const { errors, validateForm, isValid } = useFormValidation(schema);

const handleSubmit = () => {
  if (validateForm(formData)) {
    // Submit
  }
};
```

## Constants

### Schedule Presets

```typescript
import { SCHEDULE_PRESETS, TRIGGER_OPTIONS, OFFSET_PRESETS } from '../constants/schedulePresets';
```

## Local Development Domain Setup

### Why lvh.me instead of localhost?

This project uses **lvh.me** for local development instead of `localhost` due to cookie domain restrictions in RFC 6265.

**The Problem with localhost:**
- Browsers reject cookies with `domain=.localhost` for security reasons
- `localhost` is treated as a special-use domain where domain cookies don't work
- Cannot share cookies across subdomains like `platform.localhost` and `business.localhost`

**The Solution - lvh.me:**
- `lvh.me` is a public DNS service that resolves all `*.lvh.me` domains to `127.0.0.1`
- Browsers allow setting cookies with `domain=.lvh.me`
- Cookies are accessible across all subdomains (platform.lvh.me, business1.lvh.me, etc.)
- No /etc/hosts configuration needed - it just works!

### Development URLs

Use these URLs for local development:

- **Base domain:** `http://lvh.me:5173`
- **Platform dashboard:** `http://platform.lvh.me:5173`
- **Business subdomains:** `http://{subdomain}.lvh.me:5173`

### Multi-Tenant Architecture

The application uses subdomain-based multi-tenancy:

1. **Platform Users** (superuser, platform_manager, platform_support)
   - Access the app at `http://platform.lvh.me:5173`
   - See platform dashboard and administrative features

2. **Business Users** (owner, manager, staff, resource)
   - Access the app at `http://{business_subdomain}.lvh.me:5173`
   - See business-specific dashboard and features

### Cookie-Based Authentication

Tokens are stored in cookies with `domain=.lvh.me` to enable cross-subdomain access:

```typescript
// Set cookie accessible across all *.lvh.me subdomains
setCookie('access_token', token, 7);  // domain=.lvh.me

// Cookie is accessible on:
// - platform.lvh.me:5173
// - business1.lvh.me:5173
// - business2.lvh.me:5173
// etc.
```

**Key Files:**
- `/src/utils/cookies.ts` - Cookie utilities with cross-subdomain support
- `/src/hooks/useAuth.ts` - Authentication hooks using cookies
- `/src/api/client.ts` - API client with cookie-based auth

### Login Flow

1. User navigates to `http://platform.lvh.me:5173`
2. If not authenticated, shows login page
3. User enters credentials and submits
4. Backend validates and returns JWT tokens + user data
5. Tokens stored in cookies with `domain=.lvh.me`
6. User data stored in React Query cache
7. App checks user role:
   - Platform users: Stay on platform.lvh.me
   - Business users: Redirect to {business_subdomain}.lvh.me
8. Cookies accessible on target subdomain - user sees dashboard

### Testing with Playwright

Tests use lvh.me for proper subdomain testing:

```typescript
// Start on platform subdomain
await page.goto('http://platform.lvh.me:5173');

// Login sets cookies with domain=.lvh.me
await page.getByPlaceholder(/username/i).fill('poduck');
await page.getByPlaceholder(/password/i).fill('starry12');
await page.getByRole('button', { name: /sign in/i }).click();

// Cookies accessible, dashboard loads
await expect(page.getByRole('heading', { name: /platform dashboard/i })).toBeVisible();
```

### Running the Development Server

```bash
# Install dependencies
npm install

# Start dev server
npm run dev

# Access at http://platform.lvh.me:5173
```

### Running Tests

```bash
# Run all tests
npm test

# Run E2E tests
npx playwright test

# Run specific test file
npx playwright test login-flow.spec.ts

# Run with UI
npx playwright test --ui
```

## Production Deployment

In production, replace `lvh.me` with your actual domain:

1. Update `src/utils/cookies.ts`:
   ```typescript
   const domain = window.location.hostname.includes('yourdomain.com')
     ? '.yourdomain.com'
     : window.location.hostname;
   ```

2. Configure DNS:
   - `platform.yourdomain.com` → Your server IP
   - `*.yourdomain.com` → Your server IP (wildcard for business subdomains)

3. SSL certificates:
   - Get wildcard certificate for `*.yourdomain.com`
   - Or use Let's Encrypt with wildcard support

## Troubleshooting

### Cookies not working?
- Make sure you're using `lvh.me`, not `localhost`
- Check browser DevTools → Application → Cookies
- Verify `domain=.lvh.me` is set on cookies
- Clear cookies and try again

### Redirect issues?
- Check `/src/pages/LoginPage.tsx` redirect logic
- Verify user role and business_subdomain in response
- Check browser console for navigation errors

### Can't access lvh.me?
- Verify internet connection (lvh.me requires DNS lookup)
- Try `ping lvh.me` - should resolve to `127.0.0.1`
- Alternative: Use `127.0.0.1.nip.io` (similar service)

## References

- [RFC 6265 - HTTP State Management Mechanism](https://datatracker.ietf.org/doc/html/rfc6265)
- [lvh.me DNS service](http://lvh.me)
- [Cookie Domain Attribute Rules](https://stackoverflow.com/questions/1062963/how-do-browser-cookie-domains-work)
