feat(domain): create comprehensive business components for ticketing platform

- Add EventCard with glassmorphism styling and role-based actions
- Add TicketTypeRow with inline editing and inventory tracking
- Add OrderSummary with promo codes and fee breakdown integration
- Add ScanStatusBadge with real-time updates and accessibility
- Add FeeBreakdown with transparent pricing and regulatory compliance
- Create business logic types for events, tickets, orders, scanning
- Implement responsive layouts (card/table) for all screen sizes
- Ensure WCAG AA compliance with proper ARIA labels and screen reader support
- Use design tokens exclusively for consistent theming
- Build comprehensive showcase component demonstrating all features

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-16 11:54:25 -06:00
parent 6d879d0685
commit 02a5146533
13 changed files with 2257 additions and 0 deletions

View File

@@ -0,0 +1,371 @@
import React, { useState } from 'react';
import type { Order, ScanStatus } from '../types/business';
import {
MOCK_EVENTS,
MOCK_TICKET_TYPES,
DEFAULT_FEE_STRUCTURE
} from '../types/business';
import { MOCK_USERS } from '../types/auth';
import { EventCard } from './events';
import { TicketTypeRow } from './tickets';
import { OrderSummary } from './checkout';
import { ScanStatusBadge } from './scanning';
import { FeeBreakdown } from './billing';
import { Card, CardHeader, CardBody } from './ui/Card';
import { Button } from './ui/Button';
import { MainContainer } from './layout';
const DomainShowcase: React.FC = () => {
const [currentUser] = useState(MOCK_USERS[1]); // Organizer user
const [scanStatuses, setScanStatuses] = useState<ScanStatus[]>([
{
isValid: true,
status: 'valid',
timestamp: new Date().toISOString(),
ticketInfo: {
eventTitle: 'Autumn Gala & Silent Auction',
ticketTypeName: 'VIP Patron',
customerEmail: 'customer@example.com',
seatNumber: 'A12'
}
},
{
isValid: false,
status: 'used',
timestamp: new Date(Date.now() - 300000).toISOString(),
errorMessage: 'This ticket was already scanned 5 minutes ago'
},
{
isValid: false,
status: 'invalid',
errorMessage: 'QR code format is not recognized'
}
]);
// Mock order data
const mockOrder: Order = {
id: 'ord-123',
eventId: 'evt-1',
customerEmail: 'customer@example.com',
items: [
{
ticketTypeId: 'tt-1',
ticketTypeName: 'VIP Patron',
price: 35000,
quantity: 2,
subtotal: 70000
},
{
ticketTypeId: 'tt-2',
ticketTypeName: 'General Admission',
price: 15000,
quantity: 1,
subtotal: 15000
}
],
subtotal: 85000,
platformFee: 3075,
processingFee: 2495,
tax: 7875,
total: 98445,
status: 'pending',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
const handleEventAction = (action: string, eventId: string) => {
// Handle event actions in real application
};
const handleTicketAction = (action: string, ticketTypeId: string, value?: unknown) => {
// Handle ticket type actions in real application
};
const handlePromoCode = async (code: string) => {
// Apply promo code in real application
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000));
return { success: false, error: 'Promo code not found' };
};
const simulateScan = () => {
const newStatus: ScanStatus = {
isValid: Math.random() > 0.5,
status: Math.random() > 0.7 ? 'valid' : 'invalid',
timestamp: new Date().toISOString(),
errorMessage: Math.random() > 0.5 ? 'Invalid QR format' : undefined,
ticketInfo: {
eventTitle: 'Contemporary Dance Showcase',
ticketTypeName: 'General Admission',
customerEmail: 'test@example.com'
}
};
setScanStatuses(prev => [newStatus, ...prev.slice(0, 4)]);
};
return (
<MainContainer>
<div className="space-y-8">
<div className="text-center">
<h1 className="text-3xl font-bold text-fg-primary mb-4">
Domain Components Showcase
</h1>
<p className="text-lg text-fg-secondary max-w-2xl mx-auto">
Professional event ticketing components for upscale venues with glassmorphism design
</p>
</div>
{/* Event Cards Section */}
<section className="space-y-4">
<h2 className="text-2xl font-semibold text-fg-primary">Event Cards</h2>
<p className="text-fg-secondary">
Display event information with role-based actions and glassmorphism styling
</p>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{MOCK_EVENTS.map((event) => (
<EventCard
key={event.id}
event={event}
currentUser={currentUser}
onView={(id) => handleEventAction('view', id)}
onEdit={(id) => handleEventAction('edit', id)}
onManage={(id) => handleEventAction('manage', id)}
/>
))}
</div>
</section>
{/* Ticket Type Management Section */}
<section className="space-y-4">
<h2 className="text-2xl font-semibold text-fg-primary">Ticket Type Management</h2>
<p className="text-fg-secondary">
Manage ticket types with inline editing and inventory tracking
</p>
{/* Card Layout */}
<div className="space-y-4">
<h3 className="text-lg font-medium text-fg-primary">Card Layout (Mobile-Friendly)</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{MOCK_TICKET_TYPES.map((ticketType) => (
<TicketTypeRow
key={ticketType.id}
ticketType={ticketType}
layout="card"
currentUser={currentUser}
onEdit={(tt) => handleTicketAction('edit', tt.id)}
onDelete={(id) => handleTicketAction('delete', id)}
onToggleStatus={(id, status) => handleTicketAction('toggle-status', id, status)}
onQuantityUpdate={(id, quantity) => handleTicketAction('quantity-update', id, quantity)}
onPriceUpdate={(id, price) => handleTicketAction('price-update', id, price)}
/>
))}
</div>
</div>
{/* Table Layout */}
<div className="space-y-4">
<h3 className="text-lg font-medium text-fg-primary">Table Layout (Desktop)</h3>
<Card variant="elevated">
<CardBody className="p-0">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="border-b border-border-subtle">
<tr>
<th className="px-4 py-3 text-left text-sm font-semibold text-fg-primary">Ticket Type</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-fg-primary">Price</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-fg-primary">Quantity</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-fg-primary">Sold</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-fg-primary">Available</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-fg-primary">Sales Rate</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-fg-primary">Revenue</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-fg-primary">Actions</th>
</tr>
</thead>
<tbody>
{MOCK_TICKET_TYPES.map((ticketType) => (
<TicketTypeRow
key={ticketType.id}
ticketType={ticketType}
layout="table"
currentUser={currentUser}
onEdit={(tt) => handleTicketAction('edit', tt.id)}
onDelete={(id) => handleTicketAction('delete', id)}
onToggleStatus={(id, status) => handleTicketAction('toggle-status', id, status)}
onQuantityUpdate={(id, quantity) => handleTicketAction('quantity-update', id, quantity)}
onPriceUpdate={(id, price) => handleTicketAction('price-update', id, price)}
/>
))}
</tbody>
</table>
</div>
</CardBody>
</Card>
</div>
</section>
{/* Order Summary & Fee Breakdown Section */}
<section className="space-y-4">
<h2 className="text-2xl font-semibold text-fg-primary">Checkout Experience</h2>
<p className="text-fg-secondary">
Professional order summary with transparent fee breakdown
</p>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Order Summary */}
<div className="space-y-4">
<h3 className="text-lg font-medium text-fg-primary">Order Summary</h3>
<OrderSummary
order={mockOrder}
onPromoCodeApply={handlePromoCode}
onPromoCodeRemove={() => { /* Remove promo code */ }}
/>
<h4 className="text-md font-medium text-fg-primary">Compact Layout</h4>
<OrderSummary
order={mockOrder}
layout="compact"
showPromoCode={false}
/>
</div>
{/* Fee Breakdown */}
<div className="space-y-4">
<h3 className="text-lg font-medium text-fg-primary">Fee Breakdown</h3>
<FeeBreakdown
order={mockOrder}
feeStructure={DEFAULT_FEE_STRUCTURE}
showTooltips
showCalculations={false}
/>
<h4 className="text-md font-medium text-fg-primary">Table Layout</h4>
<FeeBreakdown
order={mockOrder}
layout="table"
showCalculations
/>
</div>
</div>
</section>
{/* Scanning Interface Section */}
<section className="space-y-4">
<h2 className="text-2xl font-semibold text-fg-primary">QR Scanning Interface</h2>
<p className="text-fg-secondary">
Real-time ticket validation with status indicators and animations
</p>
<div className="flex justify-center mb-6">
<Button onClick={simulateScan} variant="primary">
Simulate Ticket Scan
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{scanStatuses.map((status, index) => (
<Card key={index} variant="elevated">
<CardHeader>
<h4 className="text-md font-medium text-fg-primary">
Scan Result #{scanStatuses.length - index}
</h4>
</CardHeader>
<CardBody>
<ScanStatusBadge
scanStatus={status}
showTimestamp
showTicketInfo
animated
size="md"
/>
</CardBody>
</Card>
))}
</div>
{/* Different Sizes */}
<div className="space-y-4">
<h3 className="text-lg font-medium text-fg-primary">Badge Sizes</h3>
<div className="flex flex-wrap gap-4 items-center">
<ScanStatusBadge
scanStatus={scanStatuses[0]}
size="sm"
showTimestamp={false}
showTicketInfo={false}
animated={false}
/>
<ScanStatusBadge
scanStatus={scanStatuses[0]}
size="md"
showTimestamp={false}
showTicketInfo={false}
animated={false}
/>
<ScanStatusBadge
scanStatus={scanStatuses[0]}
size="lg"
showTimestamp={false}
showTicketInfo={false}
animated={false}
/>
</div>
</div>
</section>
{/* Usage Examples */}
<section className="space-y-4">
<h2 className="text-2xl font-semibold text-fg-primary">Usage Examples</h2>
<Card variant="elevated">
<CardHeader>
<h3 className="text-lg font-semibold text-fg-primary">Integration Code</h3>
</CardHeader>
<CardBody>
<pre className="text-sm text-fg-secondary bg-surface-secondary rounded p-4 overflow-x-auto">
{`import { EventCard, TicketTypeRow, OrderSummary, ScanStatusBadge, FeeBreakdown } from '../components';
// Event listing page
<EventCard
event={event}
currentUser={user}
onView={handleView}
onManage={handleManage}
/>
// Ticket management
<TicketTypeRow
ticketType={ticketType}
layout="card"
onEdit={handleEdit}
onQuantityUpdate={handleQuantityUpdate}
/>
// Checkout process
<OrderSummary
order={order}
onPromoCodeApply={handlePromoCode}
/>
// QR scanning
<ScanStatusBadge
scanStatus={scanResult}
animated={true}
showTicketInfo={true}
/>
// Admin fee transparency
<FeeBreakdown
order={order}
layout="table"
showCalculations={true}
/>`}
</pre>
</CardBody>
</Card>
</section>
</div>
</MainContainer>
);
};
export default DomainShowcase;