# User Guide ## How the EWA is Embedded The Booking EWA (Embedded Web App) is loaded inside a mobile app's WebView. The mobile app generates a signed JWT containing the user's session data and passes it via the `X-Payload` HTTP header when loading the EWA URL. The flow: 1. The mobile app creates a JWT (signed with HS256 using a shared secret) containing user identity, operator config, theme preference, and optional design tokens. 2. The WebView loads the EWA URL, attaching the JWT in the `X-Payload` request header. 3. The Express backend verifies the JWT and extracts session data. 4. The React frontend fetches `/api/session` to retrieve this session data. 5. The frontend applies the theme (LIGHT/DARK + custom design tokens) and renders the UI. --- ## Screens ### 1. Home (`/#/`) The landing screen of the application. Displays: - **Title**: "Bookings" - **Upcoming Bookings**: Up to 3 active bookings (status `accepted` or `reserved`) with `startAt` in the future, displayed as `BookingCard` components. Each card shows the location ID, time range, and status badge. Tapping a card navigates to the Booking Detail screen. - **Empty State**: When no upcoming bookings exist, shows a calendar icon with "No Bookings Yet" and a prompt to book a charging slot. - **"Book a Charger" Button**: Primary action that navigates to the Create Booking screen. ### 2. Create Booking (`/#/create`) A two-step flow for creating a new booking. **Step 1 -- Select Time and Location:** - **Location ID** (number input): The ID of the charging location. Enter the numeric location ID from the AMPECO platform. - **Start Time** (datetime picker): Minimum value is the current time. - **End Time** (datetime picker): Minimum value is the selected start time. - **"Check Availability" button**: Sends a POST request to check availability at the specified location for the given time range. Advances to Step 2 on success. **Step 2 -- Confirm Booking:** - **Available Slots**: Displays EVSE-grouped time slots returned by the availability check. Each EVSE shows its ID and a grid of selectable time windows. Tapping a slot populates the start/end time fields. - **EVSE Criteria** (collapsible section): Optional filters for the booking request: - Current Type: AC or DC (dropdown) - Min Power (kW) - Max Power (kW) - Connector Type (text) - **"Confirm Booking" button**: Submits a `create` booking request. **Result Screen:** After submission, displays one of: - **Approved**: Green check icon, "Booking Confirmed" message, and a "View Booking" button that navigates to the booking detail. - **Rejected**: Red X icon, "Booking Rejected" message with the rejection reason, and a "Try Again" button to reset the form. ### 3. My Bookings (`/#/bookings`) Lists the user's bookings with a tab bar: - **Upcoming Tab**: Bookings with status `accepted` or `reserved` where `startAt` is in the future. - **Past Tab**: Bookings where `endAt` is in the past (all statuses). Each booking is rendered as a `BookingCard`. Tapping a card navigates to the Booking Detail screen. Shows an empty state with a calendar icon when no bookings exist for the selected tab. ### 4. Booking Detail (`/#/bookings/:id`) Displays full details of a single booking: - **Location**: Location ID - **Time Range**: Formatted start and end times - **Status**: Color-coded badge (green for accepted, blue for reserved, gray for completed, red for cancelled/failed, amber for no-show) - **EVSEs**: List of booked EVSE IDs (if any) - **Access Methods**: List of access methods (if any) - **Session**: Linked charging session ID (if any) **Actions** (shown only when status is `accepted` or `reserved`): - **"Update Booking" button**: Navigates to the Update Booking screen. - **"Cancel Booking" button**: Opens a confirmation bottom sheet with "Yes, Cancel" and "Close" options. The detail view **polls the API every 30 seconds** to keep the status current. Polling pauses when the browser tab is hidden and stops when the booking reaches a terminal status. ### 5. Update Booking (`/#/bookings/:id/update`) Allows modifying the time range of an existing booking: - Displays a read-only summary of the booking (location ID and booking ID). - **Start Time** and **End Time** datetime pickers, pre-populated with the current booking times. - **"Save Changes" button**: Submits an `update` booking request. - On success: Shows a green "Booking Confirmed" banner. - On rejection: Shows the rejection reason or validation errors. --- ## Development Mode ### Dev Fallback Session When `NODE_ENV=development` (set in `.env`), the backend automatically provides a fallback session if no JWT is present: ```json { "userId": 775, "operatorId": 1, "appLanguage": "en", "appCountry": "US", "appTheme": "LIGHT" } ``` This means you can open `http://localhost:5173` directly in a browser without needing a JWT. The app will use user ID 775 by default. ### UserIdFallback Screen If the session has `userId: null` (which can happen with certain JWT configurations), the frontend displays a "User Identification" screen prompting you to enter a numeric user ID. This screen is shown instead of the main app. ### Generating a Dev JWT In development mode, the backend exposes a `POST /dev/jwt` endpoint for generating JWT tokens with custom session data. **Request:** ```bash curl -X POST http://localhost:3001/dev/jwt \ -H "Content-Type: application/json" \ -d '{ "userId": 123, "operatorId": 2, "appTheme": "DARK" }' ``` Any field from `SessionData` can be overridden. Omitted fields use the default dev values. **Response:** ```json { "token": "eyJhbGciOiJIUzI1NiIs..." } ``` **Using the token:** Pass it as the `X-Payload` header in subsequent requests: ```bash curl http://localhost:5173/api/session \ -H "X-Payload: eyJhbGciOiJIUzI1NiIs..." ``` Or use it in browser-based testing by setting the header in your HTTP client or browser extension. The generated JWT has a 24-hour expiry, uses HS256, and is signed with the `JWT_SECRET` from `.env`. --- ## Environment Variables Reference | Variable | Required | Default | Description | |----------------|----------|--------------------------------|----------------------------------------------------------------------| | `API_BASE_URL` | Yes | `""` | Base URL of the AMPECO public API (e.g., `https://instance.charge.ampeco.tech/public-api`) | | `API_TOKEN` | Yes | `""` | Bearer token for authenticating backend requests to the public API | | `JWT_SECRET` | Yes | `dev-secret-for-local-testing` | Secret key for HS256 JWT verification. Must match the mobile app's signing key. | | `PORT` | No | `3001` | Port for the Express backend server | | `NODE_ENV` | No | `production` | Set to `development` for dev fallback session and `/dev/jwt` endpoint | --- ## Booking Lifecycle ### Statuses | Status | Color | Description | |-------------|--------|----------------------------------------------------------| | `accepted` | Green | Booking request approved, slot is reserved for the user | | `reserved` | Blue | Booking is actively reserved at the charge point | | `completed` | Gray | Booking period ended normally | | `cancelled` | Red | Booking was cancelled by the user or system | | `no-show` | Amber | User did not show up during the booking window | | `failed` | Red | Booking request or reservation failed | ### Status Transitions ```mermaid stateDiagram-v2 [*] --> accepted : Booking request approved accepted --> reserved : Charge point reserved accepted --> cancelled : User cancels accepted --> failed : System failure reserved --> completed : Booking period ends reserved --> cancelled : User cancels reserved --> no_show : User does not show up completed --> [*] cancelled --> [*] no_show --> [*] failed --> [*] ``` Terminal statuses: `completed`, `cancelled`, `no-show`, `failed`. Once a booking reaches a terminal status, the detail view stops polling and the update/cancel actions are hidden. ### Actions by Status | Status | Can Update | Can Cancel | |-------------|------------|------------| | `accepted` | Yes | Yes | | `reserved` | Yes | Yes | | `completed` | No | No | | `cancelled` | No | No | | `no-show` | No | No | | `failed` | No | No | ### Booking Request Flow All mutations (create, update, cancel) go through the booking request mechanism: 1. The frontend submits a `BookingRequest` with a `type` field (`create`, `update`, or `cancel`). 2. The backend proxies this to `POST /resources/booking-requests/v1.0`. 3. The API returns a `BookingRequest` response with `status: 'approved'` or `status: 'rejected'`. 4. For `create` requests, an approved response includes a `bookingId` for the newly created booking. 5. For `update` requests, the booking is modified in-place. 6. For `cancel` requests, the booking transitions to `cancelled` status. --- ## Error Handling The frontend handles errors at multiple levels: - **API Client** (`src/api/client.ts`): Throws `ApiRequestError` with the HTTP status code and parsed response body. Provides convenience getters for common status codes (401, 403, 404, 422). - **Validation Errors** (422): The `validationErrors` getter extracts the `errors` object from the response body, which maps field names to arrays of error messages. The Create and Update booking forms display these inline. - **Network/Proxy Errors**: The backend returns `502 Bad Gateway` when the upstream API is unreachable. - **Session Errors**: If `/api/session` fails, the app displays an error screen with a "Retry" button that reloads the page.