- 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>
147 lines
4.6 KiB
TypeScript
147 lines
4.6 KiB
TypeScript
import { useMemo } from 'react';
|
|
|
|
import { useTicketStore } from '../stores/ticketStore';
|
|
import { useEventStore } from '../stores/eventStore';
|
|
import { useClaims } from '../hooks/useClaims';
|
|
import type { Ticket } from '../types/business';
|
|
|
|
/**
|
|
* Hook to get tickets with territory filtering applied
|
|
* Respects user role and territory access controls
|
|
*/
|
|
export const useTicketsQuery = (eventId?: string, selectedTerritoryIds?: string[]) => {
|
|
const { tickets, isLoading, error } = useTicketStore();
|
|
const { getEvent } = useEventStore();
|
|
const { claims } = useClaims();
|
|
|
|
const filteredTickets = useMemo(() => {
|
|
if (!claims) {
|
|
return [];
|
|
}
|
|
|
|
let ticketsToFilter = tickets;
|
|
|
|
// Filter by event if specified
|
|
if (eventId) {
|
|
ticketsToFilter = ticketsToFilter.filter(ticket => ticket.eventId === eventId);
|
|
}
|
|
|
|
// Apply territory filtering based on role
|
|
if (claims.role === 'territoryManager') {
|
|
// Territory managers can only see tickets for events in their territories
|
|
const userTerritoryIds = claims.territoryIds || [];
|
|
ticketsToFilter = ticketsToFilter.filter(ticket => {
|
|
const event = getEvent(ticket.eventId);
|
|
return event && userTerritoryIds.includes(event.territoryId || '');
|
|
});
|
|
} else if (claims.role === 'orgAdmin' || claims.role === 'superadmin') {
|
|
// Admins can optionally filter by selected territories
|
|
if (selectedTerritoryIds && selectedTerritoryIds.length > 0) {
|
|
ticketsToFilter = ticketsToFilter.filter(ticket => {
|
|
const event = getEvent(ticket.eventId);
|
|
return event && selectedTerritoryIds.includes(event.territoryId || '');
|
|
});
|
|
}
|
|
}
|
|
|
|
return ticketsToFilter;
|
|
}, [tickets, eventId, claims, selectedTerritoryIds, getEvent]);
|
|
|
|
return {
|
|
tickets: filteredTickets,
|
|
isLoading,
|
|
error,
|
|
totalCount: filteredTickets.length,
|
|
isFiltered: (claims?.role === 'territoryManager') ||
|
|
(selectedTerritoryIds && selectedTerritoryIds.length > 0)
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Hook to get tickets for a specific event with territory validation
|
|
*/
|
|
export const useEventTicketsQuery = (eventId: string) => {
|
|
const { tickets, isLoading, error } = useTicketStore();
|
|
const { getEvent } = useEventStore();
|
|
const { claims } = useClaims();
|
|
|
|
const eventTickets = useMemo(() => {
|
|
if (!claims) {
|
|
return [];
|
|
}
|
|
|
|
const event = getEvent(eventId);
|
|
if (!event) {
|
|
return [];
|
|
}
|
|
|
|
// Check territory access for territory managers
|
|
if (claims.role === 'territoryManager') {
|
|
const userTerritoryIds = claims.territoryIds || [];
|
|
if (!userTerritoryIds.includes(event.territoryId || '')) {
|
|
return []; // Access denied to event, so no tickets
|
|
}
|
|
}
|
|
|
|
return tickets.filter(ticket => ticket.eventId === eventId);
|
|
}, [eventId, tickets, getEvent, claims]);
|
|
|
|
return {
|
|
tickets: eventTickets,
|
|
isLoading,
|
|
error,
|
|
hasAccess: eventTickets.length > 0 || claims?.role !== 'territoryManager'
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Hook to get ticket statistics with territory filtering
|
|
*/
|
|
export const useTicketStatsQuery = (selectedTerritoryIds?: string[]) => {
|
|
const { tickets } = useTicketStore();
|
|
const { getEvent } = useEventStore();
|
|
const { claims } = useClaims();
|
|
|
|
const stats = useMemo(() => {
|
|
if (!claims) {
|
|
return {
|
|
totalSold: 0,
|
|
totalRevenue: 0,
|
|
pendingTickets: 0,
|
|
scannedTickets: 0
|
|
};
|
|
}
|
|
|
|
let ticketsToAnalyze = tickets;
|
|
|
|
// Apply territory filtering based on role
|
|
if (claims.role === 'territoryManager') {
|
|
const userTerritoryIds = claims.territoryIds || [];
|
|
ticketsToAnalyze = ticketsToAnalyze.filter(ticket => {
|
|
const event = getEvent(ticket.eventId);
|
|
return event && userTerritoryIds.includes(event.territoryId || '');
|
|
});
|
|
} else if (claims.role === 'orgAdmin' || claims.role === 'superadmin') {
|
|
if (selectedTerritoryIds && selectedTerritoryIds.length > 0) {
|
|
ticketsToAnalyze = ticketsToAnalyze.filter(ticket => {
|
|
const event = getEvent(ticket.eventId);
|
|
return event && selectedTerritoryIds.includes(event.territoryId || '');
|
|
});
|
|
}
|
|
}
|
|
|
|
const totalSold = ticketsToAnalyze.length;
|
|
const totalRevenue = ticketsToAnalyze.reduce((sum, ticket) => sum + ticket.price, 0);
|
|
const pendingTickets = ticketsToAnalyze.filter(ticket => ticket.status === 'pending').length;
|
|
const scannedTickets = ticketsToAnalyze.filter(ticket => ticket.status === 'scanned').length;
|
|
|
|
return {
|
|
totalSold,
|
|
totalRevenue,
|
|
pendingTickets,
|
|
scannedTickets
|
|
};
|
|
}, [tickets, claims, selectedTerritoryIds, getEvent]);
|
|
|
|
return stats;
|
|
}; |