🛠️ Task Checklist
⚙️ Tooling & Fixes
- Install and configure
react-hook-form
npm install react-hook-form
🔐 Account & Authentication
- Use Axios request interceptor to attach token to requests and handle logout logic
// add a request interceptor to include JWT token if available
agent.interceptors.request.use((config) => {
const token = useAuthStore.getState().token;
if (token) {
config.headers = config.headers || {};
config.headers["Authorization"] = `Bearer ${token}`;
}
return config;
});
// add a response interceptor to handle 401 Unauthorized
agent.interceptors.response.use(
(res) => res,
(error) => {
if (error.response?.status === 401) {
useAuthStore.getState().logout();
window.location.href = "/login"; // redirect to login
}
return Promise.reject(error);
}
);
API
- Provide a method for each new endpoint implemented in backend
const Profile = {
getProfile: () => request.get<ProfileDto>('/account/profile'),
editEmail: (data: EditEmailDto) => request.put<void>('/account/email', data),
editPassword: (data: EditPasswordDto) => request.put<void>('/account/password', data),
upsertAccountPerson: (data: PersonDto) => request.post<number>('/account/account-person', data),
upsertPerson: (data: PersonDto) => request.post<number>('/account/person', data),
upsertBankDetail: (data: UpsertBankAccountDetailDto) => request.post<void>('/account/bank-detail', data),
getMyPeople: () => request.get<PersonDto[]>('/account/my-people'),
getMyTravels: () => request.get<TicketOrderSummaryDto[]>('/account/my-travels'),
getMyTransactions: () => request.get<TransactionDto[]>('/account/my-transactions'),
topUp: (data : topUpDto) => request.post<number>('/account/top-up', data)
};
🗂️ Additional Profile Tabs (initial setup)
-
Add empty pages/tabs for:
-
ProfileSummary
-
MyTravels
-
MyTransactions
-
MyPeople
-
-
Implement
ProfilePage
using the components created. This is an example of how it can be done:
import InfoAccount from "./InfoAccount";
import InfoPeople from "./InfoPeople";
import InfoTransactions from "./InfoTransactions";
import InfoTravels from "./InfoTravels";
import { useState } from "react";
const tabs = [
{ label: "Account", component: <InfoAccount /> },
{ label: "Transactions", component: <InfoTransactions /> },
{ label: "Travels", component: <InfoTravels /> },
{ label: "People", component: <InfoPeople /> }
];
const Profile = () => {
const [selected, setSelected] = useState(0);
return (
<div className="max-w-4xl mx-auto py-8 px-4">
<div className="flex flex-col md:flex-row gap-8">
<aside className="md:w-64 w-full bg-white rounded-lg shadow p-4 flex md:flex-col flex-row gap-2 md:gap-0 mb-4 md:mb-0">
{tabs.map((tab, idx) => (
<button
key={tab.label}
className={`text-right px-4 py-2 rounded transition font-medium text-base md:text-lg w-full ${
selected === idx
? "bg-blue-100 text-blue-700"
: "hover:bg-gray-100 text-gray-700"
}`}
onClick={() => setSelected(idx)}
type="button"
>
{tab.label}
</button>
))}
</aside>
<main className="flex-1 min-w-0">{tabs[selected].component}</main>
</div>
</div>
);
};
export default Profile;
-
Create prototype of
ProfilePage
and integrate with navbar. (Create a button or link to accessProfilePage
, only when user is logged in) -
Define and adjust routes for profile and its tabs
const AppRoutes = () => {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/profile" element={<Profile />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
};
export default AppRoutes;
- Implement tab handling inside
ProfilePage
. You can also do it route-based.
Profile Summary
📦 DTOs / Models
-
Add models for:
-
EditEmailDto
export interface EditEmailDto { newEmail: string; }
-
EditPasswordDto
export interface EditPasswordDto { oldPassword: string; newPassword: string; confirmNewPassword: string; }
-
PersonDto
export interface PersonDto { id?: number; creatorAccountId?: number; firstName: string; lastName: string; idNumber: string; genderId: number; phoneNumber: string; birthDate: string; }
-
ProfileDto
export interface ProfileDto { accountPhoneNumber: string; email: string; balance: number; firstName: string; lastName: string; idNumber: string; personPhoneNumber: string; birthDate: Date | string | null; iban: string; bankAccountNumber: string; cardNumber: string; }
-
UpsertBankAccountDetailDto
export interface UpsertBankAccountDto { iban?: string; bankAccountNumber?: string; cardNumber?: string; }
-
-
Implement the process of showing and editing the data. To do so, you can use modal components and set their functionality in the pages.
🧍 List of Travelers
- Implement
ListOfTravelers
page for showing, editing, and adding new people. You can use the same modal used forUpsertAccountPerson
.
💳 Transactions Module
- Add
TransactionDto
model
export interface TransactionDto {
id: number;
transactionTypeId: number;
accountId: number;
ticketOrderId?: number;
baseAmount: number;
finalAmount: number;
serialNumber: string;
createdAt: Date | string;
description?: string;
transactionType: string;
}
- Implement
MyTransactions
page Note: It is recommended to add a component to be displayed as a card, including the table of information, then use it in the page of MyTransactions
🚆 Travel Module
- Add
TicketOrderSummaryDto
model
export interface TicketOrderSummaryDto {
id: number;
serialNumber: string;
boughtAt: Date | string;
price: number;
travelStartDate: Date | string;
travelEndDate: Date | string;
fromCity: string;
toCity: string;
companyName: string;
vehicleTypeId: number;
vehicleName: string;
}
- Implement
MyTravels
page. Note that this page can be similarly implemented by a card.
🧠 Hints & Notes
🙌 Acknowledgements
- ChatGPT for snippet refinement and explanations