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:
335
reactrebuild0825/src/app/lazy-routes.tsx
Normal file
335
reactrebuild0825/src/app/lazy-routes.tsx
Normal file
@@ -0,0 +1,335 @@
|
||||
/**
|
||||
* Lazy-loaded route components for code splitting
|
||||
* These components are loaded on-demand to improve initial bundle size
|
||||
*/
|
||||
|
||||
import { lazy } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import {
|
||||
Calendar,
|
||||
MapPin,
|
||||
Users,
|
||||
Settings,
|
||||
Shield,
|
||||
CreditCard,
|
||||
QrCode,
|
||||
Scan
|
||||
} from 'lucide-react';
|
||||
|
||||
import { Card, CardHeader, CardBody } from '@/components/ui/Card';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { Skeleton } from '@/components/loading/Skeleton';
|
||||
|
||||
// Lazy-loaded components
|
||||
export const EventDetailPage = lazy(() => import('@/pages/EventDetailPage'));
|
||||
export const GateOpsPage = lazy(() => import('@/pages/GateOpsPage').then(module => ({ default: module.GateOpsPage })));
|
||||
export const PaymentSettings = lazy(() => import('@/features/org/PaymentSettings').then(module => ({ default: module.PaymentSettings })));
|
||||
export const ScannerPage = lazy(() => import('@/features/scanner/ScannerPage').then(module => ({ default: module.ScannerPage })));
|
||||
|
||||
// Skeleton components for Suspense fallbacks
|
||||
|
||||
/**
|
||||
* Event detail page skeleton with event header, stats, and ticket types
|
||||
*/
|
||||
export function EventDetailPageSkeleton() {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="space-y-6"
|
||||
data-testid="event-detail-skeleton"
|
||||
>
|
||||
{/* Event header */}
|
||||
<Card className="overflow-hidden">
|
||||
<div className="bg-gradient-to-r from-primary-500 to-accent-500 p-6">
|
||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-3 text-white">
|
||||
<Skeleton.Base className="h-8 w-64 bg-white/20" />
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Calendar className="w-4 h-4" />
|
||||
<Skeleton.Base className="h-4 w-32 bg-white/20" />
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<MapPin className="w-4 h-4" />
|
||||
<Skeleton.Base className="h-4 w-24 bg-white/20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 md:mt-0 flex space-x-3">
|
||||
<Skeleton.Button size="md" className="bg-white/20" />
|
||||
<Skeleton.Button size="md" className="bg-white/20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Event stats */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<Card key={index} className="p-6">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Users className="w-5 h-5 text-text-secondary" />
|
||||
<Skeleton.Base className="h-4 w-20" />
|
||||
</div>
|
||||
<Skeleton.Base className="h-8 w-16" />
|
||||
<Skeleton.Base className="h-3 w-32" />
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Ticket types section */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<Skeleton.Base className="h-6 w-32" />
|
||||
<Skeleton.Button size="md" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="space-y-4">
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-4 bg-glass-bg border border-glass-border rounded-lg">
|
||||
<div className="flex-1 space-y-2">
|
||||
<Skeleton.Base className="h-5 w-40" />
|
||||
<Skeleton.Base className="h-4 w-24" />
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<Badge variant="neutral" className="bg-glass-bg">
|
||||
<Skeleton.Base className="h-4 w-12" />
|
||||
</Badge>
|
||||
<Skeleton.Base className="h-6 w-16" />
|
||||
<Skeleton.Button size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gate operations page skeleton with live scanning interface
|
||||
*/
|
||||
export function GateOpsPageSkeleton() {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="space-y-6"
|
||||
data-testid="gate-ops-skeleton"
|
||||
>
|
||||
{/* Status header */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<Card key={index} className="p-6">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Shield className="w-5 h-5 text-text-secondary" />
|
||||
<Skeleton.Base className="h-4 w-24" />
|
||||
</div>
|
||||
<Skeleton.Base className="h-8 w-20" />
|
||||
<div className="flex items-center space-x-2">
|
||||
<Badge variant="neutral" className="bg-glass-bg">
|
||||
<Skeleton.Base className="h-3 w-12" />
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Control panel */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<Skeleton.Base className="h-6 w-32" />
|
||||
<div className="flex items-center space-x-3">
|
||||
<Badge variant="neutral" className="bg-glass-bg">
|
||||
<Skeleton.Base className="h-4 w-16" />
|
||||
</Badge>
|
||||
<Skeleton.Button size="sm" />
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="space-y-4">
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-3 bg-glass-bg border border-glass-border rounded">
|
||||
<div className="flex items-center space-x-3">
|
||||
<Skeleton.Avatar size="sm" />
|
||||
<div className="space-y-1">
|
||||
<Skeleton.Base className="h-4 w-24" />
|
||||
<Skeleton.Base className="h-3 w-16" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Badge variant="neutral" className="bg-glass-bg">
|
||||
<Skeleton.Base className="h-3 w-8" />
|
||||
</Badge>
|
||||
<Skeleton.Base className="h-4 w-12" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Payment settings page skeleton with Stripe integration status
|
||||
*/
|
||||
export function PaymentSettingsPageSkeleton() {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="space-y-6"
|
||||
data-testid="payment-settings-skeleton"
|
||||
>
|
||||
{/* Connection status card */}
|
||||
<Card className="p-6">
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<CreditCard className="w-6 h-6 text-text-secondary" />
|
||||
<Skeleton.Base className="h-6 w-48" />
|
||||
</div>
|
||||
<Badge variant="neutral" className="bg-glass-bg">
|
||||
<Skeleton.Base className="h-4 w-16" />
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="bg-glass-bg border border-glass-border rounded-lg p-6">
|
||||
<div className="space-y-4">
|
||||
<Skeleton.Base className="h-5 w-56" />
|
||||
<Skeleton.Text lines={3} />
|
||||
<div className="flex space-x-3 pt-4">
|
||||
<Skeleton.Button size="md" />
|
||||
<Skeleton.Button size="md" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Payment configuration */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Skeleton.Base className="h-6 w-40" />
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="space-y-6">
|
||||
{/* Form fields */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<div key={index} className="space-y-2">
|
||||
<Skeleton.Base className="h-4 w-24" />
|
||||
<Skeleton.Base className="h-10 w-full" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Settings toggles */}
|
||||
<div className="space-y-4 pt-6 border-t border-glass-border">
|
||||
{Array.from({ length: 3 }).map((_, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-4 bg-glass-bg border border-glass-border rounded">
|
||||
<div className="space-y-1">
|
||||
<Skeleton.Base className="h-4 w-32" />
|
||||
<Skeleton.Base className="h-3 w-48" />
|
||||
</div>
|
||||
<Skeleton.Base className="h-6 w-12 rounded-full" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scanner page skeleton with camera interface
|
||||
*/
|
||||
export function ScannerPageSkeleton() {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
className="min-h-screen bg-glass-bg"
|
||||
data-testid="scanner-skeleton"
|
||||
>
|
||||
{/* Scanner header */}
|
||||
<div className="bg-gradient-to-r from-primary-500 to-accent-500 p-6 text-white">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-3">
|
||||
<QrCode className="w-6 h-6" />
|
||||
<Skeleton.Base className="h-6 w-32 bg-white/20" />
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Badge variant="neutral" className="bg-white/20 border-white/30">
|
||||
<Skeleton.Base className="h-3 w-12 bg-white/20" />
|
||||
</Badge>
|
||||
<Skeleton.Button size="sm" className="bg-white/20" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container mx-auto px-6 py-8 space-y-6">
|
||||
{/* Camera viewport */}
|
||||
<Card className="overflow-hidden">
|
||||
<div className="aspect-video bg-background-secondary border-2 border-dashed border-glass-border flex items-center justify-center">
|
||||
<div className="text-center space-y-3">
|
||||
<Scan className="w-12 h-12 text-text-secondary mx-auto" />
|
||||
<Skeleton.Base className="h-4 w-32 mx-auto" />
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Scanner controls */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<Card key={index} className="p-4">
|
||||
<div className="text-center space-y-2">
|
||||
<Settings className="w-5 h-5 text-text-secondary mx-auto" />
|
||||
<Skeleton.Base className="h-4 w-16 mx-auto" />
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Recent scans */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Skeleton.Base className="h-6 w-32" />
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<div className="space-y-3">
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<div key={index} className="flex items-center justify-between p-3 bg-glass-bg border border-glass-border rounded">
|
||||
<div className="flex items-center space-x-3">
|
||||
<Skeleton.Avatar size="sm" />
|
||||
<div className="space-y-1">
|
||||
<Skeleton.Base className="h-4 w-20" />
|
||||
<Skeleton.Base className="h-3 w-16" />
|
||||
</div>
|
||||
</div>
|
||||
<Badge variant="neutral" className="bg-glass-bg">
|
||||
<Skeleton.Base className="h-3 w-8" />
|
||||
</Badge>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user