feat: comprehensive project completion and documentation

- Enhanced event creation wizard with multi-step validation
- Added advanced QR scanning system with offline support
- Implemented comprehensive territory management features
- Expanded analytics with export functionality and KPIs
- Created complete design token system with theme switching
- Added 25+ Playwright test files for comprehensive coverage
- Implemented enterprise-grade permission system
- Enhanced component library with 80+ React components
- Added Firebase integration for deployment
- Completed Phase 3 development goals substantially

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-26 15:04:37 -06:00
parent aa81eb5adb
commit 8ed7ae95d1
230 changed files with 24072 additions and 3395 deletions

View File

@@ -4,6 +4,7 @@
* rapid scanning rate limits, and QR code quality scenarios for actual gate operations
*/
/// <reference path="../src/types/global.d.ts" />
import { test, expect } from '@playwright/test';
test.describe('Network Handoff Scenarios', () => {
@@ -37,17 +38,17 @@ test.describe('Network Handoff Scenarios', () => {
// Monitor connection type changes
if ('connection' in navigator) {
const {connection} = navigator;
window.networkTransitionTest.networkTypes.push(connection.effectiveType);
window.networkTransitionTest!.networkTypes!.push(connection.effectiveType || 'unknown');
connection.addEventListener('change', () => {
window.networkTransitionTest.connectionChanges++;
window.networkTransitionTest.networkTypes.push(connection.effectiveType);
connection?.addEventListener?.('change', () => {
window.networkTransitionTest!.connectionChanges!++;
window.networkTransitionTest!.networkTypes!.push(connection.effectiveType || 'unknown');
});
}
// Monitor online/offline events
window.addEventListener('online', () => {
window.networkTransitionTest.lastSyncTime = Date.now();
window.networkTransitionTest!.lastSyncTime = Date.now();
});
});
@@ -146,18 +147,18 @@ test.describe('Network Handoff Scenarios', () => {
fetch('/api/ping')
.then(() => {
const latency = performance.now() - startTime;
window.networkAdaptation.qualityChanges.push({
window.networkAdaptation!.qualityChanges!.push({
latency,
timestamp: Date.now(),
quality: latency < 100 ? 'fast' : latency < 500 ? 'medium' : 'slow'
});
if (latency > 1000) {
window.networkAdaptation.adaptationMade = true;
window.networkAdaptation!.adaptationMade = true;
}
})
.catch(() => {
window.networkAdaptation.qualityChanges.push({
window.networkAdaptation!.qualityChanges!.push({
latency: 999999,
timestamp: Date.now(),
quality: 'offline'
@@ -190,7 +191,7 @@ test.describe('Network Handoff Scenarios', () => {
console.log('Network adaptation data:', adaptationData);
// Should detect quality changes
expect(adaptationData.qualityChanges.length).toBeGreaterThan(2);
expect(adaptationData?.qualityChanges?.length || 0).toBeGreaterThan(2);
// Scanner should remain functional
await expect(page.locator('text=Online')).toBeVisible();
@@ -225,15 +226,15 @@ test.describe('Background/Foreground Transitions', () => {
};
document.addEventListener('visibilitychange', () => {
window.appLifecycleTest.visibilityChanges++;
window.appLifecycleTest!.visibilityChanges!++;
if (document.visibilityState === 'hidden') {
window.appLifecycleTest.backgroundTime = Date.now();
window.appLifecycleTest!.backgroundTime = Date.now();
} else if (document.visibilityState === 'visible') {
if (window.appLifecycleTest.backgroundTime > 0) {
const backgroundDuration = Date.now() - window.appLifecycleTest.backgroundTime;
window.appLifecycleTest.backgroundTime = backgroundDuration;
window.appLifecycleTest.cameraRestored = true;
if ((window.appLifecycleTest!.backgroundTime || 0) > 0) {
const backgroundDuration = Date.now() - (window.appLifecycleTest!.backgroundTime || 0);
window.appLifecycleTest!.backgroundTime = backgroundDuration;
window.appLifecycleTest!.cameraRestored = true;
}
}
});
@@ -281,8 +282,8 @@ test.describe('Background/Foreground Transitions', () => {
const lifecycleData = await page.evaluate(() => window.appLifecycleTest);
console.log('App lifecycle data:', lifecycleData);
expect(lifecycleData.visibilityChanges).toBeGreaterThanOrEqual(2);
expect(lifecycleData.cameraRestored).toBe(true);
expect(lifecycleData?.visibilityChanges || 0).toBeGreaterThanOrEqual(2);
expect(lifecycleData?.cameraRestored).toBe(true);
});
test('should preserve scan queue during background transitions', async ({ page }) => {
@@ -381,7 +382,7 @@ test.describe('Background/Foreground Transitions', () => {
try {
newWakeLock = await navigator.wakeLock.request('screen');
} catch (e) {
console.log('Wake lock re-request failed:', e.message);
console.log('Wake lock re-request failed:', (e as Error).message);
}
// Cleanup
@@ -397,7 +398,7 @@ test.describe('Background/Foreground Transitions', () => {
} catch (error) {
return {
supported: true,
error: error.message
error: (error as Error).message
};
}
}
@@ -501,8 +502,8 @@ test.describe('Multi-Device Race Conditions', () => {
// First scan
await page.evaluate((data) => {
const now = Date.now();
window.scanPrevention.scanAttempts++;
window.scanPrevention.lastScanTime = now;
window.scanPrevention!.scanAttempts!++;
window.scanPrevention!.lastScanTime = now;
window.dispatchEvent(new CustomEvent('mock-scan', {
detail: { qr: data.qr, timestamp: now }
@@ -514,16 +515,16 @@ test.describe('Multi-Device Race Conditions', () => {
// Rapid second scan (should be prevented)
await page.evaluate((data) => {
const now = Date.now();
const timeSinceLastScan = now - window.scanPrevention.lastScanTime;
const timeSinceLastScan = now - (window.scanPrevention!.lastScanTime || 0);
if (timeSinceLastScan < 2000) { // Less than 2 seconds
window.scanPrevention.preventedScans++;
window.scanPrevention!.preventedScans!++;
console.log('Preventing duplicate scan within rate limit');
return;
}
window.scanPrevention.scanAttempts++;
window.scanPrevention.lastScanTime = now;
window.scanPrevention!.scanAttempts!++;
window.scanPrevention!.lastScanTime = now;
window.dispatchEvent(new CustomEvent('mock-scan', {
detail: { qr: data.qr, timestamp: now }
@@ -536,8 +537,8 @@ test.describe('Multi-Device Race Conditions', () => {
const preventionData = await page.evaluate(() => window.scanPrevention);
console.log('Scan prevention data:', preventionData);
expect(preventionData.preventedScans).toBeGreaterThan(0);
expect(preventionData.scanAttempts).toBe(1); // Only one actual scan
expect(preventionData?.preventedScans || 0).toBeGreaterThan(0);
expect(preventionData?.scanAttempts || 0).toBe(1); // Only one actual scan
});
test('should handle concurrent offline queue sync', async ({ page }) => {
@@ -635,22 +636,22 @@ test.describe('Rapid Scanning Rate Limits', () => {
for (let i = 0; i < rapidScanCount; i++) {
await page.evaluate((data) => {
const now = Date.now();
window.rateLimitMonitor.scansAttempted++;
window.rateLimitMonitor.scanTimes.push(now);
window.rateLimitMonitor!.scansAttempted!++;
window.rateLimitMonitor!.scanTimes!.push(now);
// Simulate rate limiting logic
const recentScans = window.rateLimitMonitor.scanTimes.filter(
const recentScans = (window.rateLimitMonitor!.scanTimes || []).filter(
time => now - time < 1000 // Last 1 second
);
if (recentScans.length > 8) {
window.rateLimitMonitor.scansBlocked++;
window.rateLimitMonitor.rateLimitWarnings++;
window.rateLimitMonitor!.scansBlocked!++;
window.rateLimitMonitor!.rateLimitWarnings!++;
console.log('Rate limit exceeded - scan blocked');
return;
}
window.rateLimitMonitor.scansProcessed++;
window.rateLimitMonitor!.scansProcessed!++;
window.dispatchEvent(new CustomEvent('mock-scan', {
detail: { qr: data.qr, timestamp: now }
}));
@@ -665,12 +666,12 @@ test.describe('Rapid Scanning Rate Limits', () => {
console.log('Rate limit data:', rateLimitData);
// Should have blocked excessive scans
expect(rateLimitData.scansBlocked).toBeGreaterThan(0);
expect(rateLimitData.scansProcessed).toBeLessThan(rateLimitData.scansAttempted);
expect(rateLimitData.rateLimitWarnings).toBeGreaterThan(0);
expect(rateLimitData?.scansBlocked || 0).toBeGreaterThan(0);
expect(rateLimitData?.scansProcessed || 0).toBeLessThan(rateLimitData?.scansAttempted || 0);
expect(rateLimitData?.rateLimitWarnings || 0).toBeGreaterThan(0);
// Should not process more than ~8 scans per second
expect(rateLimitData.scansProcessed).toBeLessThan(15);
expect(rateLimitData?.scansProcessed || 0).toBeLessThan(15);
});
test('should show "slow down" message for excessive scanning', async ({ page }) => {
@@ -683,8 +684,8 @@ test.describe('Rapid Scanning Rate Limits', () => {
// Listen for rate limit events
window.addEventListener('rate-limit-warning', () => {
window.rateLimitUI.warningsShown++;
window.rateLimitUI.lastWarningTime = Date.now();
window.rateLimitUI!.warningsShown!++;
window.rateLimitUI!.lastWarningTime = Date.now();
});
});
@@ -710,8 +711,8 @@ test.describe('Rapid Scanning Rate Limits', () => {
console.log('Rate limit UI data:', uiData);
// Should show warnings for excessive scanning
if (uiData.warningsShown > 0) {
expect(uiData.warningsShown).toBeGreaterThan(0);
if ((uiData?.warningsShown || 0) > 0) {
expect(uiData?.warningsShown || 0).toBeGreaterThan(0);
}
// Scanner should remain functional
@@ -818,18 +819,18 @@ test.describe('QR Code Quality and Edge Cases', () => {
await page.evaluate((qr) => {
// Simulate QR validation logic
if (!qr || qr.trim().length === 0) {
window.qrErrorHandling.emptyScans++;
window.qrErrorHandling!.emptyScans!++;
console.log('Empty QR code detected');
return;
}
if (qr.includes('\n') || qr.includes('\t') || qr.includes('\0')) {
window.qrErrorHandling.invalidScans++;
window.qrErrorHandling!.invalidScans!++;
console.log('Invalid QR code format detected');
return;
}
window.qrErrorHandling.handledGracefully++;
window.qrErrorHandling!.handledGracefully!++;
window.dispatchEvent(new CustomEvent('mock-scan', {
detail: { qr, timestamp: Date.now(), quality: 'poor' }
@@ -843,7 +844,8 @@ test.describe('QR Code Quality and Edge Cases', () => {
console.log('QR error handling data:', errorData);
// Should detect and handle invalid QR codes
expect(errorData.emptyScans + errorData.invalidScans).toBeGreaterThan(0);
expect(errorData).toBeDefined();
expect((errorData?.emptyScans || 0) + (errorData?.invalidScans || 0)).toBeGreaterThan(0);
// Scanner should remain stable despite invalid inputs
await expect(page.locator('h1:has-text("Ticket Scanner")')).toBeVisible();