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,97 @@
import { test, expect } from '@playwright/test';
test('masquerade functionality works end-to-end', async ({ page }) => {
// Listen for console logs and errors
page.on('console', msg => console.log(`BROWSER ${msg.type().toUpperCase()}:`, msg.text()));
page.on('pageerror', err => console.log('PAGE ERROR:', err.message));
// Listen for network requests
page.on('response', async response => {
if (response.url().includes('masquerade') || response.url().includes('current')) {
console.log(`API ${response.status()} ${response.url()}`);
try {
const body = await response.text();
console.log(`RESPONSE BODY: ${body.substring(0, 500)}`);
} catch (e) {
// Response body already consumed
}
}
});
// Step 1: Login as superuser
console.log('Step 1: Login as superuser');
await page.goto('http://platform.lvh.me:5174/');
await page.waitForLoadState('networkidle');
// Fill login form
await page.locator('#username').fill('poduck');
await page.locator('#password').fill('starry12');
await page.getByRole('button', { name: /sign in/i }).click();
// Wait for redirect to dashboard
await page.waitForURL(/platform\.lvh\.me/, { timeout: 10000 });
// Verify we're on platform dashboard
await expect(page).toHaveURL(/platform\.lvh\.me/);
console.log('✓ Logged in successfully');
// Step 2: Navigate to Businesses tab
console.log('Step 2: Navigate to Businesses tab');
await page.getByRole('link', { name: /businesses/i }).click();
await page.waitForTimeout(1000);
// Wait for table to load
await page.waitForSelector('table tbody tr', { timeout: 10000 });
console.log('✓ Businesses page loaded');
// Step 3: Click masquerade on first business
console.log('Step 3: Click masquerade button');
const firstMasqueradeButton = page.getByRole('button', { name: /masquerade/i }).first();
await firstMasqueradeButton.waitFor({ state: 'visible' });
// Get business name before clicking
const firstRow = page.locator('table tbody tr').first();
const businessName = await firstRow.locator('td').first().textContent();
console.log(`Masquerading as owner of: ${businessName}`);
//Click the button
await firstMasqueradeButton.click();
// Step 4: Wait for redirect
console.log('Step 4: Wait for redirect to business subdomain');
await page.waitForURL(/lvh\.me/, { timeout: 10000 });
const finalURL = page.url();
console.log(`Final URL: ${finalURL}`);
// Verify we're on a business subdomain (not platform)
expect(finalURL).not.toContain('platform.lvh.me');
expect(finalURL).toContain('.lvh.me');
console.log('✓ Redirected to business subdomain');
// Step 5: Wait for page to load and verify we see business dashboard
await page.waitForLoadState('networkidle', { timeout: 15000 });
// Check for elements that should be on business dashboard
await page.waitForTimeout(2000);
const pageContent = await page.content();
// Should not see platform-specific content
expect(pageContent.toLowerCase()).not.toContain('platform dashboard');
console.log('✓ Business dashboard loaded');
// Step 6: Verify Customers link is visible
console.log('Step 6: Verify Customers link is visible');
const customersLink = page.getByRole('link', { name: /customers/i });
// Wait for navigation to be ready
await page.waitForTimeout(1000);
await expect(customersLink).toBeVisible({ timeout: 5000 });
console.log('✓ Customers link is visible on business dashboard');
// Take final screenshot
await page.screenshot({ path: '/tmp/masquerade-success.png', fullPage: true });
console.log('Screenshot saved to /tmp/masquerade-success.png');
});