From a7a9b22bbef5c0091400989e222203ef50c5f168 Mon Sep 17 00:00:00 2001 From: Roba Boru Date: Fri, 10 Jul 2026 21:53:42 +0300 Subject: [PATCH] Update contact information --- .../src/app/booking/results/loading.tsx | 103 ++++++++++++++++++ .../portal/src/app/booking/search/page.tsx | 64 +++++++++-- .../portal/src/app/contact/page.tsx | 4 +- .../portal/src/components/AppSidebar.tsx | 18 ++- .../portal/src/lib/generate-voucher.ts | 2 +- 5 files changed, 171 insertions(+), 20 deletions(-) create mode 100644 apps/edr-passenger-web/portal/src/app/booking/results/loading.tsx diff --git a/apps/edr-passenger-web/portal/src/app/booking/results/loading.tsx b/apps/edr-passenger-web/portal/src/app/booking/results/loading.tsx new file mode 100644 index 000000000..72a278a1a --- /dev/null +++ b/apps/edr-passenger-web/portal/src/app/booking/results/loading.tsx @@ -0,0 +1,103 @@ +// Next.js renders this automatically the instant navigation to /booking/results +// begins — before the route's JS has even finished downloading/compiling and +// well before the page component mounts or its data fetch starts. That closes +// the "I clicked Search and nothing happened" gap: previously there was no +// visual feedback at all until the route fully loaded and hit its own isLoading +// state. Mirrors that same isLoading skeleton so the transition is seamless. +export default function ResultsLoading() { + return ( +
+
+
+
+
+
+
+
+
+
+

+ Searching for trains... +

+

+ Finding the best options for your journey +

+
+
+
+
+
+
+
+ +
+ {[1, 2, 3].map((i) => ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))} +
+
+
+
+ ); +} diff --git a/apps/edr-passenger-web/portal/src/app/booking/search/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/search/page.tsx index 59ae5f3f8..d8e485fd6 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/search/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/search/page.tsx @@ -552,6 +552,17 @@ export default function SearchPage() { "origin" | "destination" | null >(null); const [hasInteracted, setHasInteracted] = useState(false); + // Immediate feedback the moment Search is clicked — router.push() itself + // doesn't paint anything until the target route's JS has loaded, which + // otherwise reads as a dead click. + const [isSearching, setIsSearching] = useState(false); + + // Warms the results route's JS chunk ahead of time so clicking Search + // doesn't have to wait for it to download/compile on top of the actual + // search request. + useEffect(() => { + router.prefetch("/booking/results"); + }, [router]); const [recentStationIds, setRecentStationIds] = useState(() => { try { return JSON.parse(localStorage.getItem("edr_recent_stations") || "[]"); @@ -689,6 +700,7 @@ export default function SearchPage() { const onSubmit = (data: SearchForm) => { setHasInteracted(true); + setIsSearching(true); // Clear previous booking selections and search cache before starting a new search clearBooking(); setSearchCriteria(data); @@ -715,6 +727,7 @@ export default function SearchPage() { // origin/destination/date errors, which is noisier than fixing things one step at a time. const onInvalid = (formErrors: typeof errors) => { setHasInteracted(true); + setIsSearching(false); const hasOtherErrors = Object.keys(formErrors).some((k) => k !== "nationality"); if (formErrors.nationality && !hasOtherErrors) { setPassengerModalOpen(true); @@ -1066,11 +1079,20 @@ export default function SearchPage() { )}
@@ -1207,11 +1229,20 @@ export default function SearchPage() { {/* Search */}
) : ( @@ -1328,11 +1359,20 @@ export default function SearchPage() { {/* Search */}
)} diff --git a/apps/edr-passenger-web/portal/src/app/contact/page.tsx b/apps/edr-passenger-web/portal/src/app/contact/page.tsx index 669a99d0b..9f0d2d474 100644 --- a/apps/edr-passenger-web/portal/src/app/contact/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/contact/page.tsx @@ -275,8 +275,8 @@ export default function Contact() { }; const contactInfo = [ - { icon: Phone, title: t('contact.phone'), value: '+251 911 000 000', link: 'tel:+251911000000' }, - { icon: Mail, title: t('contact.email'), value: 'support@edr.et', link: 'mailto:support@edr.et' }, + { icon: Phone, title: t('contact.phone'), value: '9546', link: 'tel:9546' }, + { icon: Mail, title: t('contact.email'), value: 'edr_@edrsc.com', link: 'mailto:edr_@edrsc.com' }, { icon: MapPin, title: t('contact.address'), value: 'Addis Ababa, Ethiopia', link: '#' }, ]; diff --git a/apps/edr-passenger-web/portal/src/components/AppSidebar.tsx b/apps/edr-passenger-web/portal/src/components/AppSidebar.tsx index 6d47b8b5a..0acb5d9ea 100644 --- a/apps/edr-passenger-web/portal/src/components/AppSidebar.tsx +++ b/apps/edr-passenger-web/portal/src/components/AppSidebar.tsx @@ -14,12 +14,18 @@ import { } from 'lucide-react'; import Link from 'next/link'; import Image from 'next/image'; +import dynamic from 'next/dynamic'; import { usePathname } from 'next/navigation'; import { useEffect, useState } from 'react'; import { useAuthStore } from '@/lib/auth-store'; -import ChangePasswordModal from '@/components/ChangePasswordModal'; import { BOOKING_STEPS } from '@/components/ProgressIndicator'; +// AppSidebar renders on every page via the root layout, so anything imported +// here ships to every visitor's first load — but this modal is only ever +// reachable by an already-authenticated user opening the account dropdown. +// Code-split it out instead of paying for it on every page/every visitor. +const ChangePasswordModal = dynamic(() => import('@/components/ChangePasswordModal'), { ssr: false }); + // Mirrors booking/layout.tsx's stepMap — the linear booking flow routes that // get a vertical step list instead of the standard nav highlighting. const BOOKING_STEP_MAP: Record = { @@ -203,10 +209,12 @@ export default function AppSidebar() { )}
- setShowChangePassword(false)} - /> + {showChangePassword && ( + setShowChangePassword(false)} + /> + )} ); } diff --git a/apps/edr-passenger-web/portal/src/lib/generate-voucher.ts b/apps/edr-passenger-web/portal/src/lib/generate-voucher.ts index 6ff31aeaf..ca22538be 100644 --- a/apps/edr-passenger-web/portal/src/lib/generate-voucher.ts +++ b/apps/edr-passenger-web/portal/src/lib/generate-voucher.ts @@ -358,7 +358,7 @@ function drawFooter(doc: jsPDF, createdAt: string): void { hairline(doc, PAGE_MARGIN, footerY, pageWidth - PAGE_MARGIN); doc.setFontSize(7.5); doc.setTextColor(...MUTED); doc.setFont('helvetica', 'normal'); - doc.text('support@edr.com · +251-11-XXX-XXXX · www.edr.com', pageWidth / 2, footerY + 6, { align: 'center' }); + doc.text('edr_@edrsc.com · 9546 · www.edr.com', pageWidth / 2, footerY + 6, { align: 'center' }); doc.setFontSize(6.5); doc.text(`Issued ${new Date(createdAt).toLocaleString('en-US')}`, pageWidth / 2, footerY + 10.5, { align: 'center' }); }