Problem Statement
Build a movie seat picker like BookMyShow. Show the theater layout, let the user pick seats up to a limit, and show the price before they confirm.
Functional Requirements
- Seat map: Show 10 rows (A to K, skipping I) of 15 columns, with aisle gaps at columns 4 and 13.
- Categories: Rows A to C are Premium at ₹350. The other rows are Standard at ₹250. Booked seats are grey and cannot be clicked.
- Toggle selection: Clicking an available seat selects it (green). Clicking it again deselects it.
- Seat limit: The user can pick at most
maxSelectableSeatsseats (6 in the demo). Picking one more shows an error message. - Summary: Show the count, the sorted seat ids (for example
A1, A2), the total price and a per category breakdown. - Confirm: "Proceed" is disabled with no seats picked. Confirming calls
onSelectionConfirmand clears the selection.
Technical Constraints
- Write it in TypeScript with typed
Seat,SeatLayoutandSeatStatus. - No backend. The layout is generated on the client and selection state lives in
TheaterLayout.
Overview
This system provides a complete theater seat selection interface with the following components:
- Seat layout generation
- Interactive seat grid
- Screen indicator
- Seat legend
- Selection summary
- Booking functionality
Table of Contents
Data Structure
TheaterData
interface TheaterData {
id: string;
name: string;
screen: {
id: string;
name: string;
};
showtime: {
id: string;
startTime: string;
endTime: string;
};
seatLayout: SeatLayout;
}SeatLayout
interface SeatLayout {
rows: number;
columns: number;
rowLabels: string[];
columnLabels: number[];
categories: {
id: string;
name: string;
color: string;
price: number;
}[];
seats: Seat[][];
}Seat
interface Seat {
id: string;
row: number;
column: number;
rowLabel: string;
columnLabel: number;
status: SeatStatus;
categoryId: string;
price: number;
isAisle: boolean;
}SeatStatus (Enum)
enum SeatStatus {
AVAILABLE = "available",
BOOKED = "booked",
BLOCKED = "blocked",
NOT_A_SEAT = "not_a_seat",
}Helper Functions
generateSeatLayout()
Generates a realistic theater seat layout with:
- 10 rows (A-K, skipping I)
- 15 columns
- Aisles after columns 3 and 12
- Premium seats (first 3 rows)
- Standard seats (remaining rows)
- Random booked seats (10% chance + specific patterns)
Code Flow:
- Initialize empty layout array
- Loop through row labels (A-K)
- For each row:
- Create 15 columns
- Mark columns 3 and 12 as aisles
- Set premium/standard category based on row
- Apply booking patterns
- Push seat objects to row array
- Push each row to layout array
- Return complete layout
Main Components
TheaterLayout
The main container component that orchestrates all other components.
Props
interface TheaterLayoutProps {
theaterData: SeatLayout;
maxSelectableSeats: number;
onSelectionConfirm: (seats: Seat[]) => void;
}State
selectedSeats: Array of currently selected seatsvalidationError: Current error message (if any)
Key Functions
-
handleSeatClick(seat: Seat)- Toggles seat selection
- Handles max seat limit validation
- Updates selected seats state
-
handleConfirmSelection()- Validates at least one seat is selected
- Calls
onSelectionConfirmwith selected seats - Clears the selection after confirming
Component Structure
- Renders all child components
- Manages state and passes data down
- Handles user interactions
SeatGrid
Displays the interactive seat layout.
Props
interface SeatGridProps {
seatData: Seat[][];
selectedSeats: Seat[];
onSeatClick: (seat: Seat) => void;
}Key Functions
-
isSeatSelected(seatId: string)- Checks if seat is in selectedSeats array
-
getSeatClasses(seat: Seat)- Returns appropriate CSS classes based on:
- Seat status
- Category
- Selection state
- Aisle status
- Returns appropriate CSS classes based on:
Component Structure
- Column headers (numbers)
- Row labels (letters)
- Seat grid with clickable seats
- Visual representation of aisles
ScreenIndicator
Visual representation of the theater screen.
Props
None
Component Structure
- Gradient rectangle with "Screen" label
- Shadow effect below
- Instructional text
SeatLegend
Visual key explaining seat colors and statuses.
Props
None
Component Structure
- Four legend items:
- Premium seats
- Standard seats
- Selected seats
- Unavailable seats
Each item shows:
- Colored square
- Label
- Price (where applicable)
SelectionSummary
Displays current selection and booking controls.
Props
interface SelectionSummaryProps {
selectedSeats: Seat[];
onConfirm: () => void;
maxSeats: number;
}Key Functions
getCategoryBreakdown(selectedSeats: Seat[])- Groups seats by category
- Calculates counts and totals
Component Structure
- Selected seats count
- List of selected seat IDs
- Price breakdown by category
- Total price
- Back/Proceed buttons
Utility Functions
getCurrentSelection()
Returns currently selected seats (stub implementation)
lockSeat(seatId: string)
Simulates seat locking (5 minute expiry)
unlockSeat(seatId: string)
Simulates seat unlocking
verifyBooking(seatIds: string[])
Simulates booking verification
Flow Diagram
Usage Example
import TheaterLayout from "./TheaterLayout";
import { theaterData } from "./theaterData";
function App() {
const handleConfirm = (seats: Seat[]) => {
console.log("Confirmed seats:", seats);
// Proceed to payment
};
return (
<TheaterLayout
theaterData={theaterData}
maxSelectableSeats={6}
onSelectionConfirm={handleConfirm}
/>
);
}State Management Details
Selection State Flow
-
Initial State:
selectedSeats: Empty arrayvalidationError: null
-
Seat Selection:
Loading diagram… -
Validation Rules:
- Max seats enforced in
handleSeatClick - Minimum seats enforced in
handleConfirmSelection
- Max seats enforced in
Persistence Considerations
- Current implementation is in-memory only
- Production implementation would:
- Sync with backend via API calls
- Use session storage for temporary persistence
- Implement seat locking mechanism
Error Handling
Validation Scenarios
-
Max Seats Reached:
- Message: "You can select a maximum of X seats"
- Trigger: When attempting to select beyond maxSelectableSeats
-
No Seats Selected:
- Message: "Please select at least one seat"
- Trigger: When clicking confirm with empty selection
-
Seat Conflict:
- Message: "Some seats are no longer available: [seat list]"
- Trigger: During booking verification (stub implementation)
Error Display
- Rendered in red banner above selection summary
- Auto-dismissed on next valid action
Accessibility Considerations
The current code does not set these attributes or handle arrow keys yet. This is what to add when the interviewer asks about accessibility.
ARIA Attributes
- SeatGrid:
role="grid" aria-label="Theater seating chart" - Individual Seats:
role="gridcell" aria-label={`${status} seat ${rowLabel}${columnLabel}`} aria-disabled={status !== SeatStatus.AVAILABLE}
Keyboard Navigation
- Tab Sequence:
- Seat grid → Legend → Back button → Proceed button
- Grid Navigation:
- Arrow keys move between seats
- Enter/space toggles selection
Color Contrast
- Meets WCAG AA standards:
- Premium seats: Yellow-100 on Yellow-800
- Standard seats: Blue-100 on Blue-800
- Selected seats: White on Green-500
Performance Optimizations
Memoization
// In SelectionSummary:
const totalPrice = useMemo(() => {...}, [selectedSeats]);
const formattedSeats = useMemo(() => {...}, [selectedSeats]);Virtualization
- For large theaters (>20 rows), consider:
import { FixedSizeGrid as Grid } from "react-window";
Rendering Optimizations
Only the two useMemo calls above are in the code. Next steps if the grid gets slow:
- Wrap SeatGrid in React.memo
- Make ScreenIndicator a pure component
- Memoize the category breakdown
Testing Strategy
Unit Tests
-
generateSeatLayout():
- Verifies correct seat count
- Checks aisle placement
- Validates premium/standard distribution
-
SeatGrid:
- Renders correct rows/columns
- Handles click events properly
- Displays correct seat states