# Travlr Getaways Admin — React + TypeScript SPA

CS 499 Enhancement One (Software Design and Engineering). This application is a full migration of the original Angular admin SPA (`app_admin/`) to React 18 with TypeScript, built with Vite and tested with Jest and React Testing Library. It consumes the same Express REST API at `http://localhost:3000/api` and preserves all original functionality: trip listing, add trip, edit trip, and JWT login.

## Running the application

```
cd app_react
npm install
npm run dev      # serves on http://localhost:4200 (same port as the Angular CLI)
npm test         # runs the Jest suite (16 tests)
npm run build    # type-checks and produces a production bundle in dist/
```

The Express backend must be running (`npm start` from the project root) with MongoDB available.

## Architecture mapping: Angular to React

| Angular (app_admin) | React (app_react) | Notes |
|---|---|---|
| `app.component.ts` + `<router-outlet>` | `App.tsx` + `<Routes>` | Root shell |
| `app.routes.ts` | Route table in `App.tsx` | `edit-trip` now carries `:tripCode` as a URL parameter |
| `app.config.ts` providers | `AuthProvider` + module imports | React has no DI container |
| `TripDataService` (HttpClient + RxJS) | `api/trip-api.ts` (Axios + async/await) | Typed Promise-based CRUD |
| `JwtInterceptor` (HTTP_INTERCEPTORS) | Axios request/response interceptors in `api/client.ts` | Response interceptor adds centralized 401 handling |
| `AuthenticationService` | `context/AuthContext.tsx` (`useAuth()` hook) | Login is awaitable; removes the 3-second polling timer |
| `BROWSER_STORAGE` InjectionToken | `context/token-storage.ts` module | Single seam around localStorage |
| `TripListingComponent` (signals, `ngOnInit`) | `pages/TripListing.tsx` (`useState`, `useEffect`) | Adds loading/error UI states |
| `TripCardComponent` (`@Input trip: any`) | `components/TripCard.tsx` (typed `Trip` prop) | Description rendered as escaped text, not `[innerHTML]` |
| `AddTripComponent` + `EditTripComponent` (duplicated FormGroups) | Shared `components/TripForm.tsx` | One controlled form, field metadata drives validation |
| `LoginComponent` | `pages/Login.tsx` | Awaits login and surfaces failures to the user |

## Design improvements over the original

- **Type safety end to end.** The `Trip` interface is enforced from the Axios response through context and into component props; the original passed `any` into `TripCardComponent`.
- **Eliminated duplication.** The add and edit forms shared roughly 240 duplicated lines across two components and templates; both now render one `TripForm`.
- **Route-driven edit flow.** The original stashed the trip code in `localStorage` to communicate between components; the code now travels in the URL, making edit pages bookmarkable and refresh-safe.
- **Removed a race condition.** Login resolves as a Promise after the token is committed, replacing the original `setTimeout` polling workaround.
- **Closed an XSS vector.** Trip descriptions were bound with `[innerHTML]`; React renders them as escaped text.
- **Tested.** Jest + React Testing Library cover token handling, card rendering (including the XSS regression case), and form validation.
