feat: add advanced analytics and territory management system

- Add comprehensive analytics components with export functionality
- Implement territory management with manager performance tracking
- Add seatmap components for venue layout management
- Create customer management features with modal interface
- Add advanced hooks for dashboard flags and territory data
- Implement seat selection and venue management utilities
- Add type definitions for ticketing and seatmap systems

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-26 09:25:10 -06:00
parent d5c3953888
commit aa81eb5adb
438 changed files with 90509 additions and 2787 deletions

View File

@@ -9,9 +9,185 @@
name="description"
content="Premium event ticketing platform with beautiful glassmorphism design"
/>
<!-- PWA Manifest -->
<link rel="manifest" href="/manifest.json?v=3" />
<!-- PWA Theme Colors -->
<meta name="theme-color" content="#6366f1" />
<meta name="background-color" content="#0f0f23" />
<!-- PWA Meta Tags -->
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="BCT Scanner" />
<script>
// Prevent FOUC by applying cached branding and theme before page render
(function() {
const THEME_KEY = 'bct-theme';
const BRANDING_CACHE_KEY = 'bct_branding';
// Default theme
let theme = 'dark';
try {
// 1. Apply cached organization branding immediately (if available)
const cachedBranding = localStorage.getItem(BRANDING_CACHE_KEY);
if (cachedBranding) {
const brandingData = JSON.parse(cachedBranding);
// Check cache age (expire after 24 hours)
const cacheAge = Date.now() - (brandingData.timestamp || 0);
const maxCacheAge = 24 * 60 * 60 * 1000; // 24 hours
if (cacheAge <= maxCacheAge && brandingData.theme) {
// Apply cached organization colors immediately
const root = document.documentElement.style;
const theme = brandingData.theme;
// Apply core organization colors
if (theme.accent) root.setProperty('--color-accent', theme.accent);
if (theme.bgCanvas) root.setProperty('--color-bg-canvas', theme.bgCanvas);
if (theme.bgSurface) root.setProperty('--color-bg-surface', theme.bgSurface);
if (theme.textPrimary) root.setProperty('--color-text-primary', theme.textPrimary);
if (theme.textSecondary) root.setProperty('--color-text-secondary', theme.textSecondary);
if (theme.border) root.setProperty('--color-border-default', theme.border);
if (theme.ring) root.setProperty('--color-focus-ring', theme.ring);
// Apply derived colors
if (theme.accent) {
// Generate accent variants
const accentHover = adjustColorBrightness(theme.accent, -0.1);
const accentBg = addAlphaToHex(theme.accent, 0.1);
const accentBorder = addAlphaToHex(theme.accent, 0.3);
root.setProperty('--color-accent-hover', accentHover);
root.setProperty('--color-accent-bg', accentBg);
root.setProperty('--color-accent-border', accentBorder);
}
if (theme.bgSurface && theme.textSecondary) {
// Generate glass effect colors
const glassBg = addAlphaToHex(theme.bgSurface, 0.7);
const glassBorder = addAlphaToHex(theme.textSecondary, 0.15);
root.setProperty('--color-glass-bg', glassBg);
root.setProperty('--color-glass-border', glassBorder);
}
console.log('Applied cached organization branding:', brandingData.orgId);
} else {
// Cache expired, remove it
localStorage.removeItem(BRANDING_CACHE_KEY);
}
}
// 2. Apply light/dark theme preference
const stored = localStorage.getItem(THEME_KEY);
if (stored === 'light' || stored === 'dark') {
theme = stored;
} else {
// Check system preference
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
theme = 'light';
}
}
} catch (e) {
console.warn('Error applying cached branding/theme:', e);
// Continue with defaults if localStorage fails
}
// Set theme class immediately on html element
document.documentElement.classList.toggle('dark', theme === 'dark');
document.documentElement.setAttribute('data-theme', theme);
// Utility functions for color manipulation (inline to avoid dependencies)
function adjustColorBrightness(hex, adjustment) {
try {
const color = hex.replace('#', '');
const r = Math.max(0, Math.min(255, parseInt(color.substr(0, 2), 16) + Math.round(255 * adjustment)));
const g = Math.max(0, Math.min(255, parseInt(color.substr(2, 2), 16) + Math.round(255 * adjustment)));
const b = Math.max(0, Math.min(255, parseInt(color.substr(4, 2), 16) + Math.round(255 * adjustment)));
return '#' + r.toString(16).padStart(2, '0') + g.toString(16).padStart(2, '0') + b.toString(16).padStart(2, '0');
} catch (e) {
return hex;
}
}
function addAlphaToHex(hex, alpha) {
try {
const color = hex.replace('#', '');
const r = parseInt(color.substr(0, 2), 16);
const g = parseInt(color.substr(2, 2), 16);
const b = parseInt(color.substr(4, 2), 16);
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
} catch (e) {
return hex;
}
}
})();
</script>
</head>
<body>
<div id="root"></div>
<!-- Early organization bootstrap -->
<script type="module">
// Import and run organization bootstrap as early as possible with global timeout
const bootstrapTimeout = setTimeout(() => {
console.warn('Organization bootstrap took too long, continuing without it');
}, 3000); // 3 second global timeout
import('./src/theme/orgBootstrap.ts').then(module => {
module.bootstrapOrganization()
.then(() => {
clearTimeout(bootstrapTimeout);
console.log('Organization bootstrap completed');
})
.catch(error => {
clearTimeout(bootstrapTimeout);
console.error('Organization bootstrap failed:', error);
});
}).catch(error => {
clearTimeout(bootstrapTimeout);
console.error('Failed to load organization bootstrap:', error);
});
</script>
<script>
window.addEventListener('error', e => {
const msg = (e && e.message) || 'Unknown error';
const el = document.createElement('div');
el.style.cssText='position:fixed;inset:0;background:#1b1d1f;color:#f2f3f4;padding:24px;z-index:999999;font-family:ui-sans-serif';
el.innerHTML = '<h3>App crashed</h3><pre style="white-space:pre-wrap">'+msg+'</pre>';
document.body.appendChild(el);
});
</script>
<script type="module" src="/src/main.tsx"></script>
<!-- Service Worker Registration -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then((registration) => {
console.log('SW registered: ', registration);
// Listen for SW messages
navigator.serviceWorker.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SYNC_COMPLETE') {
console.log('Background sync completed at:', new Date(event.data.timestamp));
// Dispatch custom event for scan queue to listen to
window.dispatchEvent(new CustomEvent('sw-sync-complete', {
detail: event.data
}));
}
});
})
.catch((registrationError) => {
console.log('SW registration failed: ', registrationError);
});
});
}
</script>
</body>
</html>