For International Travelers:
diff --git a/apps/edr-passenger-web/portal/src/app/login/page.tsx b/apps/edr-passenger-web/portal/src/app/login/page.tsx
index 8eacd9c10..b4becfa40 100644
--- a/apps/edr-passenger-web/portal/src/app/login/page.tsx
+++ b/apps/edr-passenger-web/portal/src/app/login/page.tsx
@@ -9,6 +9,8 @@ import { useBookingStore } from '@/lib/booking-store';
import { useState } from 'react';
import { Train } from 'lucide-react';
+export const dynamic = 'force-dynamic';
+
const loginSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(6, 'Password must be at least 6 characters'),
diff --git a/apps/edr-passenger-web/portal/src/app/profile/page.tsx b/apps/edr-passenger-web/portal/src/app/profile/page.tsx
index 67d15f30a..01a5dc45e 100644
--- a/apps/edr-passenger-web/portal/src/app/profile/page.tsx
+++ b/apps/edr-passenger-web/portal/src/app/profile/page.tsx
@@ -5,9 +5,9 @@ import { useRouter } from 'next/navigation';
import { useAuthStore } from '@/lib/auth-store';
import { useTheme } from '@/components/ThemeProvider';
import {
- User, Settings, Ticket, ChevronRight, Calendar, MapPin,
- Download, Trash2, Lock, Bell, CreditCard, Globe,
- MapPinned, Palette, CheckCircle, XCircle, Clock,
+ User, Settings, Ticket, Calendar, MapPin,
+ Download, Trash2, Lock, Bell, CreditCard,
+ MapPinned, Palette, CheckCircle,
Eye, Edit, LogOut, X
} from 'lucide-react';
import { apiClient } from '@/lib/api-client';
@@ -84,11 +84,11 @@ export default function ProfilePage() {
}
}, [isInitialized, isAuthenticated, user, router, fetchProfile]);
- const { data: bookings, isLoading: loadingBookings } = useQuery({
+ const { data: bookings, isLoading: loadingBookings } = useQuery
({
queryKey: ['user-bookings'],
- queryFn: async () => {
+ queryFn: async (): Promise => {
try {
- return await apiClient.get('/bookings/my-bookings');
+ return await apiClient.get('/bookings/my-bookings') as Booking[];
} catch {
return [];
}
@@ -98,7 +98,7 @@ export default function ProfilePage() {
const updateProfileMutation = useMutation({
mutationFn: (data: any) => apiClient.patch('/auth/profile', data),
- onSuccess: (response) => {
+ onSuccess: (response: any) => {
const updatedData = response.data || response;
updateUser(updatedData);
setShowEditProfile(false);
@@ -143,7 +143,7 @@ export default function ProfilePage() {
const downloadDataMutation = useMutation({
mutationFn: () => apiClient.get('/auth/download-data'),
- onSuccess: (data) => {
+ onSuccess: (data: any) => {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
diff --git a/apps/edr-passenger-web/portal/src/components/AppHeader.tsx b/apps/edr-passenger-web/portal/src/components/AppHeader.tsx
index 326570d97..145b5daca 100644
--- a/apps/edr-passenger-web/portal/src/components/AppHeader.tsx
+++ b/apps/edr-passenger-web/portal/src/components/AppHeader.tsx
@@ -1,15 +1,13 @@
'use client';
-import { Train, LogOut, User, BookOpen, LogIn } from 'lucide-react';
+import { Train, User, BookOpen, LogIn } from 'lucide-react';
import ThemeToggle from './ThemeToggle';
import Link from 'next/link';
import { useAuthStore } from '@/lib/auth-store';
-import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
export default function AppHeader() {
const { user, isAuthenticated, initialize } = useAuthStore();
- const router = useRouter();
useEffect(() => {
initialize();
diff --git a/apps/edr-passenger-web/portal/src/components/DualCalendarPicker.tsx b/apps/edr-passenger-web/portal/src/components/DualCalendarPicker.tsx
index cc437e251..4c8a7f24f 100644
--- a/apps/edr-passenger-web/portal/src/components/DualCalendarPicker.tsx
+++ b/apps/edr-passenger-web/portal/src/components/DualCalendarPicker.tsx
@@ -29,7 +29,7 @@ export default function DualCalendarPicker({
}: DualCalendarPickerProps) {
const [isOpen, setIsOpen] = useState(false);
const [calendarType, setCalendarType] = useState<'gregorian' | 'ethiopian'>('gregorian');
- const [currentDate, setCurrentDate] = useState(value || new Date());
+ const [, setCurrentDate] = useState(value || new Date());
const [viewMonth, setViewMonth] = useState(value?.getMonth() || new Date().getMonth());
const [viewYear, setViewYear] = useState(value?.getFullYear() || new Date().getFullYear());
diff --git a/apps/edr-passenger-web/portal/src/components/schedules/ScheduleTable.tsx b/apps/edr-passenger-web/portal/src/components/schedules/ScheduleTable.tsx
index 420372a53..3d8b49242 100644
--- a/apps/edr-passenger-web/portal/src/components/schedules/ScheduleTable.tsx
+++ b/apps/edr-passenger-web/portal/src/components/schedules/ScheduleTable.tsx
@@ -1,11 +1,19 @@
-import type { Passenger } from "@edr/types";
import { Table, type TableColumn } from "@edr/ui-common";
-export interface ScheduleTableProps {
- schedules: Passenger.ISchedule[];
+interface ISchedule {
+ id: string;
+ trainCode: string;
+ status: string;
+ departureTime: string;
+ arrivalTime: string;
+ basePrice: number;
}
-const columns: TableColumn[] = [
+export interface ScheduleTableProps {
+ schedules: ISchedule[];
+}
+
+const columns: TableColumn[] = [
{ key: "trainCode", header: "Train" },
{ key: "status", header: "Status" },
{
diff --git a/apps/edr-passenger-web/portal/src/lib/ethiopian-calendar.ts b/apps/edr-passenger-web/portal/src/lib/ethiopian-calendar.ts
index ee762d9a8..aad31a4c5 100644
--- a/apps/edr-passenger-web/portal/src/lib/ethiopian-calendar.ts
+++ b/apps/edr-passenger-web/portal/src/lib/ethiopian-calendar.ts
@@ -99,7 +99,7 @@ export function ethiopianToGregorian(ethDate: EthiopianDate): Date {
/**
* Get day of year from date (1-366)
*/
-function getDayOfYear(date: Date): number {
+export function getDayOfYear(date: Date): number {
const start = new Date(date.getFullYear(), 0, 0);
const diff = date.getTime() - start.getTime();
const oneDay = 1000 * 60 * 60 * 24;
@@ -109,7 +109,7 @@ function getDayOfYear(date: Date): number {
/**
* Convert day of year to Date object
*/
-function dayOfYearToDate(year: number, dayOfYear: number): Date {
+export function dayOfYearToDate(year: number, dayOfYear: number): Date {
const date = new Date(year, 0);
date.setDate(dayOfYear);
return date;
diff --git a/apps/edr-passenger-web/portal/src/main.tsx b/apps/edr-passenger-web/portal/src/main.tsx
deleted file mode 100644
index 67557625f..000000000
--- a/apps/edr-passenger-web/portal/src/main.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { StrictMode } from "react";
-import { createRoot } from "react-dom/client";
-import { BrowserRouter } from "react-router-dom";
-import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
-
-import App from "./App";
-
-const queryClient = new QueryClient();
-
-createRoot(document.getElementById("root")!).render(
-
-
-
-
-
-
- ,
-);
diff --git a/apps/edr-passenger-web/portal/src/pages/dashboard/DashboardPage.tsx b/apps/edr-passenger-web/portal/src/pages/dashboard/DashboardPage.tsx
deleted file mode 100644
index 71bc2e539..000000000
--- a/apps/edr-passenger-web/portal/src/pages/dashboard/DashboardPage.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-const DashboardPage = () => (
-
-
- Passenger Dashboard
-
-
- Daily ridership, ticket sales, occupancy by class, and schedule status go
- here.
-
-
-);
-
-export default DashboardPage;
diff --git a/apps/edr-passenger-web/portal/src/pages/passengers/PassengersPage.tsx b/apps/edr-passenger-web/portal/src/pages/passengers/PassengersPage.tsx
deleted file mode 100644
index 936b5f573..000000000
--- a/apps/edr-passenger-web/portal/src/pages/passengers/PassengersPage.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-const PassengersPage = () => (
-
-
Passengers
-
- Passenger directory. Connect to /passengers when ready.
-
-
-);
-
-export default PassengersPage;
diff --git a/apps/edr-passenger-web/portal/src/pages/schedules/ScheduleDetailPage.tsx b/apps/edr-passenger-web/portal/src/pages/schedules/ScheduleDetailPage.tsx
deleted file mode 100644
index ec31d2506..000000000
--- a/apps/edr-passenger-web/portal/src/pages/schedules/ScheduleDetailPage.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { useParams } from "react-router-dom";
-
-import { useSchedule } from "../../hooks/useSchedules";
-
-const ScheduleDetailPage = () => {
- const { id } = useParams<{ id: string }>();
- const { data, isLoading } = useSchedule(id ?? "");
-
- if (isLoading) return Loading…
;
- if (!data)
- return Schedule not found.
;
-
- return (
-
-
- Schedule {data.trainCode}
-
-
- Status
- {data.status}
- Departure
-
- {new Date(data.departureTime).toLocaleString()}
-
- Arrival
-
- {new Date(data.arrivalTime).toLocaleString()}
-
- Base price
- {data.basePrice.toFixed(2)}
-
-
- );
-};
-
-export default ScheduleDetailPage;
diff --git a/apps/edr-passenger-web/portal/src/pages/schedules/SchedulesPage.tsx b/apps/edr-passenger-web/portal/src/pages/schedules/SchedulesPage.tsx
deleted file mode 100644
index a51d63f91..000000000
--- a/apps/edr-passenger-web/portal/src/pages/schedules/SchedulesPage.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import ScheduleTable from "../../components/schedules/ScheduleTable";
-import { useSchedules } from "../../hooks/useSchedules";
-
-const SchedulesPage = () => {
- const { data, isLoading } = useSchedules();
- const items = data ?? [];
-
- return (
-
-
Schedules
- {isLoading ? (
-
Loading…
- ) : (
-
- )}
-
- );
-};
-
-export default SchedulesPage;
diff --git a/apps/edr-passenger-web/portal/src/pages/stations/StationsPage.tsx b/apps/edr-passenger-web/portal/src/pages/stations/StationsPage.tsx
deleted file mode 100644
index 53c5ae1fb..000000000
--- a/apps/edr-passenger-web/portal/src/pages/stations/StationsPage.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Table, type TableColumn } from "@edr/ui-common";
-import type { Passenger } from "@edr/types";
-
-import { useStations } from "../../hooks/useStations";
-
-const columns: TableColumn[] = [
- { key: "code", header: "Code" },
- { key: "name", header: "Name" },
- { key: "city", header: "City" },
- { key: "country", header: "Country" },
-];
-
-const StationsPage = () => {
- const { data, isLoading } = useStations();
- return (
-
-
Stations
- {isLoading ? (
-
Loading…
- ) : (
-
row.id}
- emptyMessage="No stations"
- />
- )}
-
- );
-};
-
-export default StationsPage;
diff --git a/apps/edr-passenger-web/portal/src/pages/tickets/BookTicketPage.tsx b/apps/edr-passenger-web/portal/src/pages/tickets/BookTicketPage.tsx
deleted file mode 100644
index 8f02a9264..000000000
--- a/apps/edr-passenger-web/portal/src/pages/tickets/BookTicketPage.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { useMutation, useQueryClient } from "@tanstack/react-query";
-import { useNavigate } from "react-router-dom";
-
-import TicketForm from "../../components/tickets/TicketForm";
-import { ticketsService } from "../../services/tickets.service";
-
-const BookTicketPage = () => {
- const navigate = useNavigate();
- const queryClient = useQueryClient();
-
- const mutation = useMutation({
- mutationFn: ticketsService.create,
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: ["tickets"] });
- navigate("/tickets");
- },
- });
-
- return (
-
-
- Issue ticket
-
-
-
- );
-};
-
-export default BookTicketPage;
diff --git a/apps/edr-passenger-web/portal/src/pages/tickets/TicketDetailPage.tsx b/apps/edr-passenger-web/portal/src/pages/tickets/TicketDetailPage.tsx
deleted file mode 100644
index d53614726..000000000
--- a/apps/edr-passenger-web/portal/src/pages/tickets/TicketDetailPage.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { useParams } from "react-router-dom";
-
-import { useTicket } from "../../hooks/useTickets";
-
-const TicketDetailPage = () => {
- const { id } = useParams<{ id: string }>();
- const { data, isLoading } = useTicket(id ?? "");
-
- if (isLoading) return Loading…
;
- if (!data)
- return Ticket not found.
;
-
- return (
-
-
- Ticket {data.reference}
-
-
- Passenger ID
- {data.passengerId}
- Schedule ID
- {data.scheduleId}
- Seat ID
- {data.seatId}
- Status
- {data.status}
- Issued
-
- {new Date(data.issuedAt).toLocaleString()}
-
- Price paid
- {data.pricePaid.toFixed(2)}
-
-
- );
-};
-
-export default TicketDetailPage;
diff --git a/apps/edr-passenger-web/portal/src/pages/tickets/TicketsPage.tsx b/apps/edr-passenger-web/portal/src/pages/tickets/TicketsPage.tsx
deleted file mode 100644
index b33471fac..000000000
--- a/apps/edr-passenger-web/portal/src/pages/tickets/TicketsPage.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { Link } from "react-router-dom";
-import { Button } from "@edr/ui-common";
-
-import TicketTable from "../../components/tickets/TicketTable";
-import { useTickets } from "../../hooks/useTickets";
-
-const TicketsPage = () => {
- const { data, isLoading } = useTickets();
- const items = data?.items ?? [];
-
- return (
-
-
-
Tickets
-
- Issue ticket
-
-
- {isLoading ? (
-
Loading…
- ) : (
-
- )}
-
- );
-};
-
-export default TicketsPage;
diff --git a/apps/edr-passenger-web/portal/src/utils/api.ts b/apps/edr-passenger-web/portal/src/utils/api.ts
index ca7c2031e..d84d9a124 100644
--- a/apps/edr-passenger-web/portal/src/utils/api.ts
+++ b/apps/edr-passenger-web/portal/src/utils/api.ts
@@ -1,7 +1,7 @@
import axios from "axios";
export const api = axios.create({
- baseURL: import.meta.env.VITE_API_URL,
+ baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000',
});
// TODO: integrate @edr/auth — add a request interceptor here that attaches
diff --git a/apps/edr-passenger-web/portal/src/vite-env.d.ts b/apps/edr-passenger-web/portal/src/vite-env.d.ts
deleted file mode 100644
index 29c29a188..000000000
--- a/apps/edr-passenger-web/portal/src/vite-env.d.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-///
-
-interface ImportMetaEnv {
- readonly VITE_API_URL: string;
-}
-
-interface ImportMeta {
- readonly env: ImportMetaEnv;
-}
diff --git a/apps/edr-passenger-web/portal/tsconfig.app.json b/apps/edr-passenger-web/portal/tsconfig.app.json
deleted file mode 100644
index 73df43221..000000000
--- a/apps/edr-passenger-web/portal/tsconfig.app.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "@edr/tsconfig/react.json",
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
- "useDefineForClassFields": true,
- "skipLibCheck": true
- },
- "include": ["src"]
-}
diff --git a/apps/edr-passenger-web/portal/tsconfig.node.json b/apps/edr-passenger-web/portal/tsconfig.node.json
deleted file mode 100644
index 181375c8f..000000000
--- a/apps/edr-passenger-web/portal/tsconfig.node.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "extends": "@edr/tsconfig/base.json",
- "compilerOptions": {
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
- "target": "ES2022",
- "lib": ["ES2023"],
- "module": "ESNext",
- "moduleResolution": "Bundler",
- "skipLibCheck": true,
- "allowSyntheticDefaultImports": true,
- "noEmit": true
- },
- "include": ["vite.config.ts"]
-}
diff --git a/apps/edr-passenger-web/portal/vite.config.ts b/apps/edr-passenger-web/portal/vite.config.ts
deleted file mode 100644
index 324a8dfa7..000000000
--- a/apps/edr-passenger-web/portal/vite.config.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
-
-export default defineConfig({
- plugins: [react()],
- server: {
- port: 5174,
- host: "0.0.0.0",
- },
- // test: {
- // environment: "jsdom",
- // globals: true,
- // },
-});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b6b31dd31..494264a0e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -119,10 +119,7 @@ importers:
version: 11.1.0(chokidar@4.0.3)(prettier@3.8.3)(typescript@5.9.3)
'@nestjs/testing':
specifier: ^11.1.19
- version: 11.1.23(@nestjs/common@11.1.23(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.23)(@nestjs/microservices@11.1.23)(@nestjs/platform-express@11.1.23)
- '@prisma/client':
- specifier: ^6.19.3
- version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)
+ version: 11.1.23(@nestjs/common@11.1.23(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.23)(@nestjs/platform-express@11.1.23)
'@types/bcrypt':
specifier: ^5.0.2
version: 5.0.2