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

@@ -0,0 +1,378 @@
/**
* Playwright test to diagnose ticket creation button issues
* on the event management page
*/
const { test, expect } = require('@playwright/test');
test('Diagnose ticket creation button issues', async ({ page }) => {
// Enable console logging to catch React errors
const consoleMessages = [];
const consoleErrors = [];
page.on('console', msg => {
const text = msg.text();
consoleMessages.push({ type: msg.type(), text });
if (msg.type() === 'error') {
consoleErrors.push(text);
console.log(`❌ Console Error: ${text}`);
} else if (msg.type() === 'warn') {
console.log(`⚠️ Console Warning: ${text}`);
} else if (text.includes('TicketsTab') || text.includes('ticket') || text.includes('React')) {
console.log(`🔍 Relevant Console: [${msg.type()}] ${text}`);
}
});
// Catch page errors
page.on('pageerror', error => {
console.log(`❌ Page Error: ${error.message}`);
consoleErrors.push(`Page Error: ${error.message}`);
});
console.log('🚀 Starting diagnosis...');
// Step 1: Navigate to login page
console.log('📝 Step 1: Navigating to login page...');
await page.goto('http://localhost:3000/login');
await page.waitForLoadState('networkidle');
// Step 2: Login with provided credentials
console.log('🔐 Step 2: Logging in...');
await page.fill('input[type="email"]', 'tyler@zest.is');
await page.fill('input[type="password"]', 'Test123!');
await page.click('button[type="submit"]');
// Wait for redirect to dashboard
await page.waitForURL('**/dashboard', { timeout: 10000 });
console.log('✅ Successfully logged in and redirected to dashboard');
// Step 3: Find and navigate to an event management page
console.log('🎪 Step 3: Looking for events to manage...');
// Wait for events to load
await page.waitForTimeout(2000);
// Look for event links - try multiple selectors
const eventSelectors = [
'a[href*="/events/"][href*="/manage"]',
'a[href*="/events/"]',
'[data-testid="event-card"] a',
'.event-card a',
'.bg-white\\/10 a'
];
let eventLinks = [];
for (const selector of eventSelectors) {
eventLinks = await page.locator(selector).all();
if (eventLinks.length > 0) {
console.log(`Found ${eventLinks.length} event links using selector: ${selector}`);
break;
}
}
if (eventLinks.length === 0) {
console.log('⚠️ No event links found. Checking page content...');
// Get page title and URL for debugging
const title = await page.title();
const url = page.url();
console.log(`Current page: ${title} at ${url}`);
// Try to find any clickable elements related to events
const eventText = await page.locator('text=/event/i').all();
console.log(`Found ${eventText.length} elements containing "event"`);
// Take a screenshot to see what's on the dashboard
await page.screenshot({
path: '/home/tyler/apps/bct-whitelabel/dashboard-state.png',
fullPage: true
});
console.log('Dashboard screenshot saved as dashboard-state.png');
return;
}
// Navigate to first available event management page
let eventManageUrl;
const firstLink = eventLinks[0];
eventManageUrl = await firstLink.getAttribute('href');
// Make sure it's a manage URL
if (!eventManageUrl.includes('/manage')) {
eventManageUrl = eventManageUrl + '/manage';
}
console.log(`🎯 Step 4: Navigating to event management: ${eventManageUrl}`);
await page.goto(`http://localhost:3000${eventManageUrl}`);
await page.waitForLoadState('networkidle');
// Wait a bit for React components to mount
await page.waitForTimeout(3000);
// Step 5: Check current tab and navigate to Ticketing & Access if needed
console.log('📋 Step 5: Checking current tab...');
// Look for active tab first
const activeTab = page.locator('.tab-active, [aria-selected="true"], .active').first();
if (await activeTab.isVisible()) {
const activeTabText = await activeTab.textContent();
console.log(`Current active tab: "${activeTabText}"`);
}
// Look for Ticketing & Access tab specifically
const ticketingTabSelectors = [
'text="Ticketing & Access"',
'text="Tickets"',
'[data-tab="tickets"]',
'button:has-text("Ticket")',
'a:has-text("Ticket")'
];
let ticketingTab = null;
for (const selector of ticketingTabSelectors) {
ticketingTab = page.locator(selector).first();
if (await ticketingTab.isVisible()) {
console.log(`🎫 Found Ticketing tab using selector: ${selector}`);
break;
}
ticketingTab = null;
}
if (ticketingTab) {
console.log('🎫 Clicking Ticketing & Access tab...');
await ticketingTab.click();
await page.waitForTimeout(2000);
// Check console for any new messages after tab click
console.log('Checking for console messages after tab click...');
} else {
console.log('⚠️ Ticketing & Access tab not found. Available tabs:');
// List all tabs/buttons that might be tabs
const potentialTabs = await page.locator('button, a[role="tab"], [class*="tab"]').all();
for (let i = 0; i < Math.min(potentialTabs.length, 10); i++) {
const tabText = await potentialTabs[i].textContent();
const isVisible = await potentialTabs[i].isVisible();
console.log(` Tab ${i + 1}: "${tabText}" (visible: ${isVisible})`);
}
}
// Step 6: Inspect DOM for TicketsTab component and React mounting
console.log('🔍 Step 6: Inspecting DOM for React components...');
// Check for React component indicators
const reactInspection = await page.evaluate(() => {
// Check for React DevTools
const hasReactDevTools = window.__REACT_DEVTOOLS_GLOBAL_HOOK__ !== undefined;
// Check for React roots
const reactRoots = document.querySelectorAll('[data-reactroot]');
// Check for any elements with React-like attributes or classes
const reactElements = document.querySelectorAll('[data-react*], [class*="react"], [class*="React"]');
// Look for TicketsTab specifically
const ticketsTabElements = document.querySelectorAll('[class*="TicketsTab"], [data-component*="TicketsTab"], [data-testid*="tickets"]');
// Check for Astro islands (which contain React components)
const astroIslands = document.querySelectorAll('astro-island');
// Check for script tags that might contain React components
const reactScripts = Array.from(document.scripts)
.map(s => s.src)
.filter(src => src && (src.includes('react') || src.includes('React') || src.includes('tickets') || src.includes('TicketsTab')));
// Look for any divs that might be React mount points
const mountPoints = document.querySelectorAll('div[id*="react"], div[id*="tickets"], div[data-*]');
return {
hasReactDevTools,
reactRootsCount: reactRoots.length,
reactElementsCount: reactElements.length,
ticketsTabElementsCount: ticketsTabElements.length,
astroIslandsCount: astroIslands.length,
reactScripts,
mountPointsCount: mountPoints.length,
bodyClasses: document.body.className,
documentTitle: document.title
};
});
console.log('React Environment Check:', JSON.stringify(reactInspection, null, 2));
// Step 7: Look for ticket creation buttons with detailed search
console.log('🔘 Step 7: Looking for ticket creation buttons...');
const buttonSelectors = [
'text="Create Your First Ticket Type"',
'text="Add Ticket Type"',
'text="Create Ticket"',
'button:has-text("ticket")',
'button:has-text("Ticket")',
'button[class*="ticket"]',
'[data-testid*="ticket"] button',
'.ticket button',
'button:has-text("Add")',
'button:has-text("Create")'
];
const allButtons = [];
for (const selector of buttonSelectors) {
const buttons = await page.locator(selector).all();
if (buttons.length > 0) {
console.log(`Found ${buttons.length} buttons with selector: ${selector}`);
for (let i = 0; i < buttons.length; i++) {
const button = buttons[i];
const text = await button.textContent();
const isVisible = await button.isVisible();
const isEnabled = await button.isEnabled();
allButtons.push({ button, text, isVisible, isEnabled, selector });
}
}
}
console.log(`Total potential ticket creation buttons found: ${allButtons.length}`);
// Step 8: Try to find the actual TicketsTab component mount point
console.log('⚛️ Step 8: Looking for TicketsTab component mount points...');
// Check for Astro islands that might contain the TicketsTab
const astroIslands = await page.locator('astro-island').all();
console.log(`Found ${astroIslands.length} Astro islands`);
for (let i = 0; i < astroIslands.length; i++) {
const island = astroIslands[i];
const componentName = await island.getAttribute('component-export');
const componentUrl = await island.getAttribute('component-url');
const props = await island.getAttribute('props');
console.log(`Island ${i + 1}:`);
console.log(` Component: ${componentName}`);
console.log(` URL: ${componentUrl}`);
console.log(` Props: ${props}`);
if (componentName && componentName.includes('TicketsTab')) {
console.log(`🎯 Found TicketsTab component island!`);
// Check if the island has rendered content
const islandContent = await island.innerHTML();
console.log(` Island content length: ${islandContent.length}`);
console.log(` Island content preview: ${islandContent.substring(0, 200)}...`);
}
}
// Step 9: Check for specific TicketsTab content
console.log('🎫 Step 9: Searching for TicketsTab-specific content...');
// Look for text that should be in TicketsTab
const ticketTabContent = [
'No ticket types created yet',
'Create Your First Ticket Type',
'Ticket Types',
'Add Ticket Type',
'General Admission',
'VIP',
'Early Bird'
];
for (const content of ticketTabContent) {
const element = page.locator(`text="${content}"`).first();
const isVisible = await element.isVisible();
if (isVisible) {
console.log(`✅ Found TicketsTab content: "${content}"`);
}
}
// Step 10: Try clicking ticket creation buttons if found
console.log('🖱️ Step 10: Attempting to interact with ticket creation buttons...');
const workingButtons = allButtons.filter(b => b.isVisible && b.isEnabled);
console.log(`Found ${workingButtons.length} clickable buttons`);
if (workingButtons.length > 0) {
const button = workingButtons[0];
console.log(`Attempting to click: "${button.text}" (${button.selector})`);
// Monitor console messages during click
const beforeClickMessages = consoleMessages.length;
try {
await button.button.click();
await page.waitForTimeout(2000);
// Check for new console messages
const newMessages = consoleMessages.slice(beforeClickMessages);
if (newMessages.length > 0) {
console.log('New console messages after click:');
newMessages.forEach(msg => console.log(` [${msg.type}] ${msg.text}`));
}
// Check if a modal or form appeared
const modalSelectors = [
'[role="dialog"]',
'.modal',
'[class*="modal"]',
'[class*="popup"]',
'[data-testid*="modal"]'
];
for (const selector of modalSelectors) {
const modals = await page.locator(selector).all();
if (modals.length > 0) {
console.log(`✅ Found ${modals.length} modals after click using selector: ${selector}`);
}
}
} catch (error) {
console.log(`❌ Error clicking button: ${error.message}`);
}
} else {
console.log('❌ No clickable ticket creation buttons found');
}
// Step 11: Final diagnosis and screenshot
console.log('\n📊 FINAL DIAGNOSIS:');
console.log('='.repeat(60));
console.log(`Console Errors: ${consoleErrors.length}`);
if (consoleErrors.length > 0) {
console.log('Console Errors:');
consoleErrors.forEach((error, i) => console.log(` ${i + 1}. ${error}`));
}
console.log(`\nTotal Console Messages: ${consoleMessages.length}`);
console.log(`React DevTools Available: ${reactInspection.hasReactDevTools ? '✅' : '❌'}`);
console.log(`Astro Islands Found: ${reactInspection.astroIslandsCount}`);
console.log(`React Elements Found: ${reactInspection.reactElementsCount}`);
console.log(`TicketsTab Elements Found: ${reactInspection.ticketsTabElementsCount}`);
console.log(`Potential Ticket Buttons: ${allButtons.length}`);
console.log(`Clickable Ticket Buttons: ${allButtons.filter(b => b.isVisible && b.isEnabled).length}`);
// Check for specific console messages that indicate the problem
const ticketTabMessages = consoleMessages.filter(msg =>
msg.text.includes('TicketsTab') ||
msg.text.includes('tickets') ||
msg.text.includes('React') ||
msg.text.includes('component')
);
if (ticketTabMessages.length === 0) {
console.log('\n🚨 CRITICAL FINDING: No TicketsTab console messages detected!');
console.log('This suggests the TicketsTab React component is not mounting at all.');
} else {
console.log('\nTicketsTab related console messages:');
ticketTabMessages.forEach(msg => console.log(` [${msg.type}] ${msg.text}`));
}
// Take final screenshot
console.log('\n📸 Taking final screenshot...');
await page.screenshot({
path: '/home/tyler/apps/bct-whitelabel/ticket-creation-final-diagnosis.png',
fullPage: true
});
console.log('Screenshot saved as: ticket-creation-final-diagnosis.png');
// Keep browser open briefly for manual inspection
console.log('\n🔍 Diagnosis complete. Check the screenshots and console output above.');
await page.waitForTimeout(5000);
});