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:
147
reactrebuild0825/src/queries/tickets.ts
Normal file
147
reactrebuild0825/src/queries/tickets.ts
Normal file
@@ -0,0 +1,147 @@
|
||||
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;
|
||||
};
|
||||
Reference in New Issue
Block a user