mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 12:41:04 +00:00
EDR Passenger Portal
Modern Next.js 14 web application for the Ethio-Djibouti Railway passenger booking system.
Features
Complete Booking Flow
- Search - Find trains by route, date, and passenger count
- Results - View available schedules with pricing
- Auth Check - Sign in or continue as guest
- Passengers - Collect passenger details with Fayda verification
- Seats - Select seats with visual seat map
- Review - Confirm booking details and fare breakdown
- Payment - Choose payment method and process payment
- Confirmation - View PNR, tickets with QR codes
Key Capabilities
- Fayda 2.0 Integration - Ethiopian national ID verification
- Age-Based Pricing - First child travels free
- Multi-Currency Support - ETB, DJF, USD display
- Seat Hold System - 2-hour seat reservation
- Guest Booking - Book without account, optional registration
- QR Code Tickets - Digital tickets with QR codes
- Responsive Design - Mobile-first, works on all devices
Tech Stack
- Framework: Next.js 14 with App Router
- Styling: Tailwind CSS
- State Management:
- TanStack Query (React Query) for server state
- Zustand for client state (booking flow, auth, payment)
- Forms: React Hook Form with Zod validation
- API Client: Axios with interceptors
- Date Handling: date-fns
- QR Codes: qrcode.react
Getting Started
Prerequisites
- Node.js >= 20.x
- pnpm >= 9.x
- EDR Passenger API running on port 3002
Installation
# Install dependencies
pnpm install
# Create environment file
cp .env.example .env.local
# Update .env.local with API URL
NEXT_PUBLIC_API_URL=http://localhost:3002
Development
# Run development server
pnpm dev
# Access at http://localhost:5174
Build
# Build for production
pnpm build
# Start production server
pnpm start
Project Structure
src/
├── app/ # Next.js App Router pages
│ ├── booking/
│ │ ├── search/ # Search trains
│ │ ├── results/ # Search results
│ │ ├── auth-check/ # Login or guest
│ │ ├── passengers/ # Passenger details + Fayda
│ │ ├── seats/ # Seat selection
│ │ ├── review/ # Booking review
│ │ ├── payment/ # Payment processing
│ │ └── confirmation/ # Booking confirmation
│ ├── login/ # Login page
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home (redirects to search)
│ ├── providers.tsx # React Query provider
│ └── globals.css # Global styles
├── components/ # Reusable components
├── lib/ # Core utilities
│ ├── api-client.ts # Axios client with interceptors
│ ├── auth-store.ts # Auth state (Zustand)
│ ├── booking-store.ts # Booking flow state (Zustand)
│ └── payment-store.ts # Payment state (Zustand)
├── types/ # TypeScript types
│ └── index.ts
└── hooks/ # Custom React hooks
State Management
Booking Store (Zustand)
Persists booking flow state across pages:
- Search criteria
- Selected schedule
- Passenger details
- Seat hold information
- Booking ID and PNR
- Payment method
Auth Store (Zustand)
Manages user authentication:
- User profile
- JWT token
- Login/logout/register
- Persisted to localStorage
Payment Store (Zustand)
Tracks payment flow:
- Payment intent ID
- Payment status
- Selected currency
API Integration
Endpoints Used
| Endpoint | Method | Purpose |
|---|---|---|
/stations |
GET | Fetch all stations |
/search |
POST | Search available trains |
/passengers/verify-fayda |
POST | Verify Ethiopian national ID |
/seatmap/{scheduleId} |
GET | Get coaches and seats |
/seatmap/{scheduleId}/hold |
POST | Hold seats (2 hours) |
/bookings/create |
POST | Create booking + generate PNR |
/bookings/{id}/confirm |
PATCH | Confirm booking after payment |
/payments/intent |
POST | Create payment intent |
/auth/login |
POST | User login |
/auth/register |
POST | User registration |
Booking Flow
1. Search
- User selects origin, destination, date, passengers
- Validates form with Zod schema
- Stores criteria in booking store
- Navigates to results
2. Results
- Fetches schedules from API
- Displays available trains with pricing
- User selects a schedule
- Stores selection and navigates to auth check
3. Auth Check
- Checks if user is authenticated
- Offers "Sign In" or "Continue as Guest"
- Authenticated users can use saved profiles
4. Passengers
- Collects details for each passenger
- Ethiopian nationals: Fayda verification
- Calls
/passengers/verify-fayda - Auto-fills name and DOB on success
- Allows manual entry on failure
- Calls
- Non-Ethiopians: Passport details
- Optional account creation checkbox
- Stores passenger data in booking store
5. Seats
- Fetches coaches and seat map
- Visual seat selection (4-column grid)
- Color-coded seat status:
- Green: Available
- Blue: Selected
- Yellow: Held by others
- Gray: Booked/Blocked
- Calls
/seatmap/{scheduleId}/holdon selection - Stores hold ID and expiry (2 hours)
- Option to skip (auto-assign)
6. Review
- Displays trip summary
- Lists all passengers
- Shows fare breakdown
- Displays seat hold countdown timer
- Calls
/bookings/createon confirm - Generates 6-character PNR
- Navigates to payment
7. Payment
- Displays PNR prominently
- Payment method selection:
- Telebirr
- CBE Birr
- eBirr
- Card
- Wallet
- Shows order summary
- Calls
/payments/intent - Processes payment (simulated for now)
8. Confirmation
- Calls
/bookings/{id}/confirm - Displays success message
- Shows PNR with copy button
- Generates QR codes for each ticket
- Lists all passenger tickets
- Download and share options
- "Book Another Trip" button clears state
Form Validation
All forms use React Hook Form + Zod:
// Example: Search form validation
const searchSchema = z.object({
originStationId: z.string().min(1, 'Please select origin'),
destinationStationId: z.string().min(1, 'Please select destination'),
departureDate: z.string().min(1, 'Please select date'),
adultCount: z.number().min(1).max(9),
childCount: z.number().min(0).max(9),
nationality: z.enum(['ETHIOPIAN', 'DJIBOUTIAN', 'OTHER']),
}).refine((data) => data.originStationId !== data.destinationStationId, {
message: 'Origin and destination must be different',
path: ['destinationStationId'],
});
Styling
Tailwind Utility Classes
Custom component classes in globals.css:
.btn-primary /* Primary action button */
.btn-secondary /* Secondary action button */
.input-field /* Form input styling */
.card /* Card container */
Theme Colors
Primary brand color: rgb(20, 113, 76) (EDR green)
Shades available: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900
Error Handling
- Network errors: Retry button with exponential backoff
- Validation errors: Inline field-level messages
- API errors: User-friendly error messages
- Seat hold expiry: Alert and re-selection option
- 401 Unauthorized: Auto-redirect to login
Accessibility
- Semantic HTML elements
- ARIA labels on interactive elements
- Keyboard navigation support
- Color contrast WCAG AA compliant
- Screen reader announcements for validation errors
Mobile Responsiveness
- Mobile-first design approach
- Responsive grid layouts (md: breakpoint)
- Touch-friendly button sizes
- Scrollable seat maps on small screens
- Optimized forms for mobile input
Testing Checklist
- Search form validation
- Results display and selection
- Guest vs authenticated flow
- Fayda verification (Ethiopian)
- Passport form (non-Ethiopian)
- Seat selection and hold
- Hold countdown timer
- PNR generation
- Payment method selection
- Confirmation with QR codes
- Mobile responsiveness
- Error states
- Back navigation
Environment Variables
NEXT_PUBLIC_API_URL=http://localhost:3002 # Passenger API URL
Known Limitations
- Payment processing is simulated (no real provider integration yet)
- Ticket PDF download not implemented (placeholder button)
- Share booking feature not implemented (placeholder button)
- Seat hold release on expiry requires manual refresh
- No internationalization (English only)
Future Enhancements
- Real payment provider integration (Stripe, Telebirr, etc.)
- PDF ticket generation and download
- Email/SMS sharing functionality
- Real-time seat availability updates (WebSocket)
- Booking history page
- User profile management
- Saved passenger profiles
- Multi-language support (Amharic, Arabic)
- Accessibility improvements
- Analytics tracking
Contributing
Follow the EDR Platform standards in CLAUDE.md:
- TypeScript strict mode
- Conventional commits
- ESLint + Prettier
- pnpm only (no npm/yarn)
License
Proprietary - Ethio-Djibouti Railway Platform
Support
For issues or questions, contact the EDR Platform team.