Skip to content

Repository files navigation

Roadly Angular

Angular 22 frontend for the Roadly vehicle rental platform. It consumes the sibling vehicle-rental-system Express and PostgreSQL API.

Roadly provides a persistent global display-currency preference for USD, MYR, EUR, GBP, SGD, and AUD. Rental prices retain USD as their source currency, while ride, driver earning, and payout prices retain MYR. The UI uses backend-provided indicative conversion rates; Stripe checkout and financial records continue to use the original transaction currency.

Bookings and rides capture the selected display currency and exchange rate when the customer confirms. Historical cards and receipts use that stored snapshot and can show the original USD/MYR settlement amount, so later rate or preference changes do not rewrite a customer's confirmation.

Features

Public experience

  • Responsive Roadly landing page
  • Brand-aware car, bike, van, and SUV presentation
  • Searchable vehicle catalogue with date-aware availability, sorting, type filters, and active-filter reset
  • Vehicle image URLs with type-appropriate fallbacks
  • Responsive specification-rich cards, loading skeletons, vehicle details, and similar-ride discovery
  • Customer registration and sign-in
  • Google sign-in with Better Auth and HttpOnly cookie sessions
  • Authentication restored from the API without storing tokens in browser storage
  • Cookie-authenticated Socket.IO connections and immediate authorization changes after role updates
  • Two-step phone OTP onboarding before customer booking creation
  • Protected booking creation, history, and eligible cancellation
  • Authenticated customer and driver support with rental/ride context, persistent conversations, FAQs, and automatic refresh
  • Visual vehicle selection with live date-range availability and price quotes
  • Two-month availability calendar with blocked booking dates
  • Alternative vehicle suggestions and a booking confirmation receipt
  • Pickup and return branches and times
  • Insurance, daily add-ons, special requests, promo codes, taxes, and refundable deposits
  • Optional Stripe Checkout with Resend email and Twilio SMS integration status
  • Sticky application header and full Roadly footer
  • Light, dark, and system themes persisted in local storage
  • Runtime language switching with complete English and Bahasa Melayu feature catalogs
  • Separate Roadly Rides experience for immediate Bike, Car, and XL trips in Kuala Lumpur
  • Google map location picking, signed MYR fare quotes, promotional discounts, pre-authorized cards or driver-confirmed cash settlement, and cancellation fees
  • Dedicated driver console with online status, browser GPS, navigation, and controlled trip progression
  • Driver ride rejection with a typed reactive reason form, required context for “Other,” safe automatic reassignment, and duplicate-submit protection
  • Automatic nearest-driver matching after card authorization, Socket.IO trip updates, automatic final-fare capture, and opt-in Web Push notifications
  • Passenger reassignment notices and administrator rejection audit details showing the driver, reason, optional explanation, and timestamp

Administration

  • Role-protected admin routes
  • Sticky header and responsive admin sidebar
  • Dashboard statistics
  • Fleet creation, editing, deletion, filtering, availability, specifications, location, and rating management
  • Booking details and return workflow
  • User role changes and deletion controls
  • AG Grid Community data tables
  • Server-side search, filtering, and sorting
  • Fifteen-record API batches
  • Automatic record appending when the grid reaches its scroll boundary
  • Manual First, Previous, direct page, Next, and Last navigation
  • Custom collapsible left filter panel with active-filter count and Clear all
  • Mobile off-canvas filter drawer
  • Roadly confirmation dialogs and toast notifications instead of browser alerts
  • Support inbox with customer context, booking context, filters, priority, status, replies, and pagination
  • Automatic ride matching with manual dispatch fallback, driver onboarding, live operations, automatic waiting charges, and driver-entered tolls
  • Operational dispatch tabs with live counts, compact expandable ride rows, attention filters, and direct server-side pagination
  • Dedicated driver operations route so onboarding and availability do not lengthen the ride queue

Technology

  • Angular 22 standalone components
  • TypeScript
  • Angular Router and route guards
  • Angular Forms and HTTP client
  • Transloco runtime internationalization
  • AG Grid Community
  • Spartan accessible primitives with Roadly-owned Tailwind styling
  • Spartan input, textarea, field, native-select, select, and searchable combobox primitives
  • Tailwind CSS 4 through Angular's PostCSS pipeline
  • Lucide icons through ng-icons
  • ngx-sonner notifications
  • SCSS and Roadly design tokens
  • Vitest through the Angular CLI

Local setup

Requirements

  • Node.js 22 recommended
  • npm
  • The sibling backend running on port 5000

Install dependencies:

npm install

Start the backend from vehicle-rental-system:

npm run dev

Start Roadly from this repository:

npm start

Open http://localhost:4200.

Tailwind PostCSS configuration

The root .postcssrc.json enables Tailwind CSS 4 during Angular style processing:

{
  "plugins": {
    "@tailwindcss/postcss": {}
  }
}

The inner {} is intentional: it enables the plugin with its default options. Keep this file while Tailwind utility classes or Spartan components styled with Tailwind are used.

API configuration

Angular environment files define the API base URL:

Configuration File API URL
Development src/environments/environment.ts http://localhost:5000/api/v1
Production src/environments/environment.production.ts https://express-project-iota.vercel.app/api/v1

When deploying the frontend to another origin, set the backend FRONTEND_URL environment variable to that origin.

Authentication requests use withCredentials; the backend session cookie is HttpOnly and unavailable to Angular code. Deploy the frontend and API on HTTPS, configure BACKEND_URL, and register https://<api-host>/api/v1/auth/session/callback/google as the Google OAuth redirect URI.

At application startup, Roadly calls /api/v1/auth/me before protected route guards run. A signed-out startup probe is handled silently; genuine sign-in and API errors continue to use Roadly toast messages. Signing out revokes the server session and clears only in-memory user state—authentication data is never written to localStorage.

For local Google OAuth, register this exact redirect URI:

http://localhost:5000/api/v1/auth/session/callback/google

The theme preference may still use localStorage; this is visual preference data and is unrelated to authentication.

Set googleMapsBrowserKey in the appropriate Angular environment file to a browser key restricted to the Maps JavaScript API and the deployed frontend origins. Keep the separate GOOGLE_MAPS_SERVER_KEY only in the backend environment for Routes API calls.

Application routes

Route Access Description
/ Public Roadly landing page
/vehicles Public Search and browse vehicles
/rides Customer Immediate KL ride booking
/driver Driver Assigned trips, GPS, progression, and rejection
/bookings Authenticated Customer booking history
/support Authenticated Customer or driver support inbox
/login Guest Sign in
/register Guest Create an account
/auth/callback Public Complete Google sign-in
/complete-profile Authenticated Verify customer phone
/admin/overview Admin Operations overview
/admin/vehicles Admin Fleet management
/admin/bookings Admin Booking management
/admin/users Admin User management
/admin/support Admin Support operations inbox
/admin/rides Admin Operational ride dispatch queue
/admin/drivers Admin Driver onboarding and availability

Project structure

src/app/
  components/   Route and feature components
    admin/       Admin shell and administration sections
    auth/        Login and registration
    bookings/    Customer bookings
    home/        Public landing page
    support/     Authenticated support and live inbox
    rides/       Immediate rides and Google map picker
    driver/      Driver availability and active trips
    vehicles/    Public fleet catalogue
  core/          Auth, guards, HTTP, models, interceptors, and themes
    i18n/         Transloco loader and persisted language preference
  layout/        Site header and footer
  shared/        Reusable grids, dialogs, filters, cells, and notifications

Components use separate .component.ts, .component.html, .component.scss, and .component.spec.ts files.

Form-control architecture

Application forms remain typed Angular Reactive Forms. Their visual and accessible controls use the generated Spartan Helm layer under src/app/shared/ui:

  • hlmInput for text, email, password, date, time, number, URL, telephone, and search inputs
  • hlmTextarea for multiline messages, notes, descriptions, and support replies
  • hlm-native-select for short fixed choices such as statuses, roles, vehicle types, payment methods, and filters
  • hlm-combobox for searchable or higher-cardinality choices such as verified-user onboarding and available-driver assignment
  • Spartan Field primitives for new validation layouts, descriptions, and accessible error messages

Use the appropriate Spartan primitive for future controls instead of introducing another custom/native form-control pattern. Keep business state in typed reactive forms and keep Roadly colours in the shared CSS design tokens.

Scripts

Command Description
npm start Run the Angular development server
npm test -- --watch=false Run the test suite once
npm run i18n:check Verify that every locale has the same translation keys
npm run build Create a production build
npm run watch Build continuously using the development configuration

Themes

Roadly supports light, dark, and system preferences. The selection is stored under roadly_theme, and AG Grid, dialogs, public pages, and admin sections use the same theme tokens.

Internationalization

Roadly loads translation catalogs at runtime with Transloco. Locale files are public static assets:

public/assets/i18n/
  en/
    common.json
    vehicles.json
    bookings.json
    rides.json
    support.json
    driver.json
    admin.json
  ms/
    common.json
    vehicles.json
    bookings.json
    rides.json
    support.json
    driver.json
    admin.json

English is the source and fallback locale. Bahasa Melayu is the second production-ready locale. The selected locale is stored under roadly_language; this is presentation preference data and contains no authentication information. Shared UI and the vehicle, booking, ride, support, driver, and administration feature areas use the same runtime locale. Dates, numbers, MYR ride values, and USD rental values are formatted using that locale.

To add another language:

  1. Copy the complete en/ directory to a new ISO-language directory such as es/ or pt/.
  2. Translate values in every feature catalog without changing keys or interpolation tokens such as {{year}}.
  3. Register the code and label in LanguageService, then add it to availableLangs in app.config.ts and to the parity script.
  4. Run npm run i18n:check to catch missing or unexpected keys.

Grid behavior

Admin grids retrieve data from server-side paginated endpoints. Each request loads 15 records. Scrolling to the grid boundary appends the next batch, while the paginator can jump directly to another page. Search, filters, and sorting reset the result to page one and request fresh data from the API.

The filter sidebar is a custom Roadly component because AG Grid's native Filters Tool Panel is an Enterprise feature.

Driver rejection and reassignment

An assigned driver can reject a ride before arrival by selecting Not available, Too far away, or Other. The form uses Angular typed reactive forms and Spartan button primitives; Other requires an explanation. While the request is being processed, its controls are disabled to prevent duplicate submissions.

After a successful rejection:

  • The rejecting driver’s active-trip panel is cleared.
  • Not available changes that driver to offline.
  • The passenger sees that the previous driver was unavailable and whether Roadly is still searching or has reassigned the ride.
  • The administrator sees the rejecting driver, reason, details, and timestamp in the dispatch queue.
  • Realtime updates keep passenger, administrator, and replacement-driver sessions synchronized.

Dispatch workspace

The ride dispatch route defaults to Needs action and separates active, completed, cancelled, and complete ride history into server-filtered tabs. Each tab retains 15-row API pagination with First, Previous, direct page, Next, and Last controls. Tab changes, filters, and manual pagination show compact skeleton rows while loading; background realtime refreshes remain non-blocking. Compact rows keep assignment visible while secondary payment, rejection, waiting, toll, and completed-fare details open on demand.

Operational attention filters isolate rides awaiting card authorization, rides with driver rejections, and cash trips awaiting driver confirmation. Driver onboarding and roster availability are managed separately at /admin/drivers; assignment dropdowns on /admin/rides continue to use the same live driver data.

Verification

Password recovery

The sign-in page links to /forgot-password. Angular sends a generic recovery request through Better Auth, then accepts the single-use token at /reset-password. Both screens use standalone components, typed reactive forms, Spartan inputs/buttons, theme variables, and the existing Transloco catalogs. A successful reset returns to sign-in and shows confirmation; invalid or expired links can request a replacement without exposing whether an account exists.

For local testing, start the backend, submit the forgot-password form, and use the URL printed by the backend when Resend is not configured. Production must configure RESEND_API_KEY and EMAIL_FROM.

Run the test command before committing changes that affect templates, components, or shared grid behavior. Focused component tests can be selected with Angular CLI's --include option.

Current limitations

  • Vehicle images are remote URLs rather than uploaded and managed assets
  • Browser GPS requires user permission and HTTPS outside localhost
  • Push notifications require backend VAPID keys and an explicit customer opt-in
  • Automatic matching uses the latest persisted driver position within the configured KL search radius and excludes drivers who rejected the same ride; production dispatch can later add explicit offer/accept timeouts and traffic-aware pickup ETAs

About

Roadly is an Angular 22 mobility frontend for vehicle rentals and on-demand rides, with JWT and Google authentication, bookings, ride tracking, driver dispatch, support, and admin tools. Built using standalone components, Typed Reactive Forms, Tailwind CSS, Spartan UI, Lucide icons, and ngx-sonner.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages