271 lines
9.2 KiB
Markdown
271 lines
9.2 KiB
Markdown
# Reservation System Development Guide - Session 10
|
||
|
||
This guide helps developers understand how to implement, update, and maintain the ticket reservation system. It summarizes changes from commits and provides step-by-step actions, grouped by feature area.
|
||
https://github.com/MehrdadShirvani/AlibabaClone-Frontend/commits/develop/
|
||
|
||
## Fixes and Missing Things from Session 08
|
||
### Authentication & Protected Routes
|
||
|
||
- [ ] Add session persistence in `authStore` to store token more persistently
|
||
```ts
|
||
import { create } from "zustand";
|
||
import { createJSONStorage, persist } from "zustand/middleware";
|
||
import { AuthResponseDto } from "@/shared/models/authentication/AuthResponseDto";
|
||
|
||
interface AuthState {
|
||
user: AuthResponseDto | null;
|
||
login: (user: AuthResponseDto) => void;
|
||
logout: () => void;
|
||
}
|
||
|
||
export const useAuthStore = create<AuthState>()(
|
||
persist(
|
||
(set) => ({
|
||
user: null,
|
||
login: (user: AuthResponseDto) => set({ user }),
|
||
logout: () => set({ user: null }),
|
||
}),
|
||
{
|
||
name: "auth-storage", // storage key in localStorage
|
||
storage: createJSONStorage(() => localStorage),
|
||
}
|
||
)
|
||
);
|
||
|
||
```
|
||
|
||
## Profile and User Info Enhancements
|
||
|
||
- [ ] Fix and align `birthDate` types in `PersonDto`, `TransportationSearchResult`, and `ListOfTravelers`. Make sure to use `Date | string` data type for them
|
||
|
||
## Branching
|
||
|
||
- [ ] Create the feature/themes branch based on develop
|
||
|
||
### 🎨 Theming and UI Styling
|
||
|
||
- [ ] Install and import `preline`
|
||
```bash
|
||
npm install preline
|
||
```
|
||
- [ ] Add theme colors and global styles (index.css). Take a look at their [Documentation](https://preline.co/docs/index.html)
|
||
- [ ] Add `ThemeSwitcher` and integrate it into the navbar
|
||
- [ ] If you decide to do this part after implementing pages, make sure to add theme support to the following components:
|
||
- [ ] `transportationCard`, `transportationSearchForm`, `ReviewAndConfirm`
|
||
- [ ] All modals: `LoginModal`, `RegisterModal`, `SelectFromPeopleModal`
|
||
- [ ] Profile section: `ProfilePage`, `ProfileSummary`, `PersonalInformation`, `AccountInfo`, `PersonalAccountInfo`, `BankAccountDetails`, `MyTravels`, `MyTransactions`, `ListOfTravelers`
|
||
- [ ] Reservation views and components
|
||
|
||
### Merge
|
||
- [ ] Create a PR and merge the current branch with develop
|
||
|
||
## Branching for reservation
|
||
|
||
- [ ] Create the feature/ticket-reservation branch based on develop
|
||
|
||
## Backend Agent Methods
|
||
|
||
- [ ] Add `CreateTicketOrderDto` and `CreateTravelerTicketDto`. Check backend endpoints for the dtos you need for this session
|
||
- [ ] Add `TransportationSeatDto`
|
||
- [ ] Add related methods in `TicketOrder` and add it to agent
|
||
```ts
|
||
const TicketOrder = {
|
||
create: (data : CreateTicketOrderDto) => request.post<number>('/ticketOrder/create', data),
|
||
downloadPdf: (ticketOrderId : number) => request.get<Blob>(`/ticketOrder/${ticketOrderId}/pdf`, {
|
||
responseType: 'blob',
|
||
}),
|
||
}
|
||
```
|
||
|
||
## Reservation Process & Step Management
|
||
|
||
- [ ] Implement `useReservationStore` using `zustand` to manage reservation state
|
||
```ts
|
||
import { PersonDto } from '@/shared/models/account/PersonDto';
|
||
import { CreateTravelerTicketDto } from '@/shared/models/ticketOrder/createTravelerTicketDto';
|
||
import { TransportationSearchResult } from '@/shared/models/transportation/transportationSearchResult';
|
||
import { create } from 'zustand';
|
||
|
||
const defaultTraveler: CreateTravelerTicketDto = {
|
||
id: 0,
|
||
creatorAccountId: 0,
|
||
firstName: '',
|
||
lastName: '',
|
||
genderId: 0,
|
||
birthDate: '',
|
||
idNumber: '',
|
||
phoneNumber: '',
|
||
englishFirstName: '',
|
||
englishLastName: '',
|
||
isVIP: false,
|
||
seatId: null,
|
||
description: '',
|
||
};
|
||
|
||
type ReservationStep = "transportation" | "travelers" | "review" | "payment" | "success";
|
||
|
||
interface ReservationState {
|
||
transportation: TransportationSearchResult | null;
|
||
travelers: CreateTravelerTicketDto[];
|
||
ticketOrderId: number;
|
||
couponCode: string | null;
|
||
paymentInfo: any;
|
||
people: PersonDto[],
|
||
currentStep: ReservationStep,
|
||
isConfirmed: boolean,
|
||
isPayed: boolean,
|
||
isTravelerPartDone: boolean,
|
||
addTraveler: (traveler: CreateTravelerTicketDto) => void;
|
||
setTravelers: (travelers: CreateTravelerTicketDto[]) => void;
|
||
updateTraveler: (index: number, traveler: CreateTravelerTicketDto) => void;
|
||
removeTraveler: (index: number) => void;
|
||
setTicketOrderId: (newId: number) => void;
|
||
setTransportation: (transportation: TransportationSearchResult) => void;
|
||
setCouponCode: (code: string | null) => void;
|
||
setPaymentInfo: (info: any) => void;
|
||
setPeople: (people: PersonDto[]) => void;
|
||
|
||
setCurrentStep: (step: ReservationStep) => void;
|
||
setIsConfirmed: (value: boolean) => void;
|
||
setIsPayed: (value: boolean) => void;
|
||
setIsTravelerPartDone: (value: boolean) => void;
|
||
|
||
resetReservation: () => void;
|
||
}
|
||
|
||
export const useReservationStore = create<ReservationState>((set) => ({
|
||
transportation: null,
|
||
people: [],
|
||
travelers: [defaultTraveler],
|
||
couponCode: null,
|
||
paymentInfo: null,
|
||
ticketOrderId : 0,
|
||
currentStep: "transportation",
|
||
isConfirmed: false,
|
||
isPayed : false,
|
||
isTravelerPartDone : false,
|
||
setTicketOrderId: (newId) => set(() =>({
|
||
ticketOrderId : newId
|
||
})),
|
||
addTraveler: (traveler) => set((state) => ({ travelers: [...state.travelers, traveler] })),
|
||
setTravelers: (newTravelers) => set(() => ({
|
||
travelers : newTravelers
|
||
})),
|
||
setPeople: (newPeople) => set(() => ({
|
||
people : newPeople
|
||
})),
|
||
updateTraveler: (index, traveler) =>
|
||
set((state) => {
|
||
const updated = [...state.travelers];
|
||
updated[index] = traveler;
|
||
return { travelers: updated };
|
||
}),
|
||
|
||
removeTraveler: (index) =>
|
||
set((state) => {
|
||
if (state.travelers.length <= 1) return state;
|
||
const updated = [...state.travelers];
|
||
updated.splice(index, 1);
|
||
return { travelers: updated };
|
||
}),
|
||
|
||
setTransportation: (theTransportation) => set({ transportation: theTransportation }),
|
||
setCouponCode: (code) => set({ couponCode: code }),
|
||
setPaymentInfo: (info) => set({ paymentInfo: info }),
|
||
setCurrentStep: (step) => set({ currentStep: step }),
|
||
|
||
resetReservation: () =>
|
||
set({
|
||
transportation: null,
|
||
travelers: [],
|
||
ticketOrderId: 0,
|
||
couponCode: null,
|
||
paymentInfo: null,
|
||
people: [],
|
||
currentStep: "transportation",
|
||
isConfirmed: false,
|
||
isPayed: false,
|
||
isTravelerPartDone: false
|
||
}),
|
||
|
||
setIsConfirmed: (value) => set(() => ({
|
||
isConfirmed : value
|
||
})),
|
||
setIsPayed: (value) => set(() => ({
|
||
isPayed : value
|
||
})),
|
||
setIsTravelerPartDone: (value) => set(() => ({
|
||
isTravelerPartDone : value
|
||
})),
|
||
}));
|
||
```
|
||
|
||
- [ ] Create step-based routing using `ReservationLayout`. Step-based routing, in the context of contact centers or workflow management, refers to a method of directing work items (like customer service calls or support tickets) to the most appropriate agent or resource based on a series of predefined steps or conditions.
|
||
|
||
- [ ] Add routing for reservation steps in `App.tsx`
|
||
|
||
- [ ] Add `StepIndicator` component to show step progress visually (optional)
|
||
- [ ] Add `stepGuard` logic to prevent accessing future steps prematurely
|
||
- [ ] Add logic to skip back only if previous steps are completed
|
||
|
||
- [ ] Create `TravelerForm` to gather passenger info, with the possibility to load data from the related people of the account.
|
||
|
||
- [ ] Create `TravelerDetailsForm` to gather passengers info with `TravelerForm` integration
|
||
|
||
- [ ] Create `ReviewAndConfirm` page to review selections
|
||
|
||
- [ ] Create `PaymentForm` for transaction process
|
||
|
||
- [ ] Create `TicketIssued` page for confirmation
|
||
|
||
- [ ] Add validation to show error if `seatId` is missing
|
||
|
||
## Seat Selection
|
||
- [ ] Add DTO:
|
||
```tsx
|
||
export interface transportationSeatDto{
|
||
id : number,
|
||
vehicleId : number,
|
||
row : number,
|
||
column : number,
|
||
isVIP : boolean,
|
||
isAvailable : boolean,
|
||
description : string | null,
|
||
isReserved : boolean,
|
||
genderId : number | null
|
||
}
|
||
```
|
||
|
||
- [ ] Add `getSeats()` method to `agent.ts`
|
||
- [ ] Add `SeatGridSelector` component for graphical seat layout in `TravelerDetailForm` and integrate it with traveler list, only for Buses
|
||
- [ ] Modify `TransportationCard` to integrate with seat selection
|
||
- [ ] Add `SeatOnlyGridSeatMap` for simpler seat-only display
|
||
|
||
### Coupon Integration
|
||
|
||
- [ ] Add `CouponValidationRequestDto` and `DiscountDto`
|
||
- [ ] Add `validateCoupon()` method to `agent.ts`
|
||
- [ ] Update `useReservationStore` to include `CouponCode`
|
||
- [ ] Ensure `CreateTicketOrderDto` uses `CouponCode` instead of `couponId`
|
||
- [ ] Connect coupon validation flow in `ReviewAndConfirm`
|
||
|
||
## Search, Filter, and Sort Functionality
|
||
|
||
- [ ] Add filters (company) and sorting (time or price) UI to `SearchResultPage`
|
||
- [ ] Add company logo support in result cards and filters
|
||
- [ ] Add `previous/next day` buttons for time navigation
|
||
- [ ] Add remaining capacity check to transportation cards
|
||
- [ ] Add refund policy info display
|
||
- [ ] Implement showing seat map in `TransportaionCard` using `ReadOnlySeatMap`
|
||
|
||
### 📎 Notes
|
||
|
||
- Ensure you run a full theme test after UI changes.
|
||
- Test step transitions with various invalid scenarios.
|
||
- Confirm persistent session and coupon behavior across refreshes.
|
||
- Validate all filters, sort, and search navigation works.
|
||
- Test seat selection and proper rendering of rotated layouts.
|
||
|
||
## Merge
|
||
- [ ] Create a PR and merge the current branch with develop
|