# FAME Onboarding UI (ROA Public UI)

The **FAME Onboarding UI** is a lightweight React-based web application that provides a user-friendly interface for claiming **Verifiable Credentials** issued by the FAME Marketplace platform. Users receive invitation links via email and use this interface to verify their identity and receive their credentials in a digital wallet.

## Overview

This single-page application enables:

- **OTP Verification**: Users enter a One-Time Password (OTP) sent to their email
- **QR Code Display**: Generation of QR codes for scanning with digital wallet apps
- **Credential Claiming**: Integration with the GOV API to finalize credential issuance
- **Status Monitoring**: Real-time checking of credential offering status
- **Mobile Responsive**: Optimized for both desktop and mobile devices

The application is built with **React 19**, **TypeScript**, and **Vite**, and is deployed as a static site served by **Nginx** in a Docker container.

## Quick Start

There are two primary ways to deploy this application:

### 1. Local Development (Vite Dev Server)
For development and testing purposes:
```bash
npm install
npm run start
```
→ Access at `http://localhost:5173/claim`

### 2. Docker Container (Production)
For production deployment:
```bash
npm run build
npm run docker:build
npm run docker:push
docker run -d -p 80:80 -e VITE_CLAIM_API='https://...' harbor.gftinnovation.eu/fame/roa/pubui:1.3
```
→ Access at `http://<host>/claim`

**Important Configuration Note**: The application requires two environment variables to be set for API endpoints:
- `VITE_CLAIM_API` - GOV API endpoint for credential claiming
- `VITE_STATUS_API` - Identity service endpoint for status checking

## Table of Contents

- [Overview](#overview)
- [Architecture](#architecture)
- [Prerequisites](#prerequisites)
- [Environment Configuration](#environment-configuration)
- [Development](#development)
- [Building for Production](#building-for-production)
- [Docker Deployment](#docker-deployment)
- [Kubernetes Deployment](#kubernetes-deployment)
- [Deployment Checklist](#deployment-checklist)
- [User Flow](#user-flow)
- [Troubleshooting](#troubleshooting)
- [License](#license)
- [Project Status](#project-status)

## Architecture

### Technology Stack

- **React 19** - UI framework
- **TypeScript** - Type-safe JavaScript
- **Vite** - Build tool and dev server
- **React Responsive** - Responsive design utilities
- **Nginx** - Production web server (in Docker)

### Application Flow

1. User receives email with invitation link containing `iid` (Invitation ID) parameter
2. User opens link in browser: `https://roa.fame-horizon.eu/claim/?iid=<invitation-id>`
3. Application extracts `iid` from URL query parameters
4. User enters OTP (received via email)
5. Application validates OTP via GOV API
6. On success, displays QR code for wallet app to scan
7. Wallet app retrieves credential using the QR code data
8. Application monitors credential status via Identity service
9. Success confirmation displayed once credential is claimed

### Build Configuration

The application uses **Vite** with a custom base path (`/claim`) to support deployment under a subpath. During Docker build:

1. **Build time**: Vite compiles React/TypeScript to optimized JavaScript
2. **Container startup**: Entrypoint script replaces placeholder values with actual API URLs from environment variables
3. **Runtime**: Nginx serves static files and handles client-side routing

## Prerequisites

- **Node.js** 22.x or higher (for local development)
- **npm** (comes with Node.js)
- **Docker** (for containerized deployment)
- For deployment to the GFT hosting infrastructure: access to **Harbor registry** at `harbor.gftinnovation.eu`

## Environment Configuration

The application requires two API endpoint configurations:

### Local Development

Create a `.env` file in the project root:

```bash
VITE_CLAIM_API=https://gov.fame-horizon.eu/api/v1.0/invitations
VITE_STATUS_API=https://identity.fame-horizon.eu/webapp/credential-offer-status
```

These variables are used directly by Vite during development.

### Production (Docker/Kubernetes)

For production deployments, environment variables are injected at **container startup** (not build time):

```bash
VITE_CLAIM_API=https://gov.fame-horizon.eu/api/v1.0/invitations
VITE_STATUS_API=https://identity.fame-horizon.eu/webapp/credential-offer-status
```

The entrypoint script ([entrypoint.sh](entrypoint.sh)) replaces placeholder strings in the compiled JavaScript files with actual values from these environment variables.

**Why this approach?**
- Allows same Docker image to be used across different environments
- API URLs can be configured at deployment time without rebuilding
- Supports ConfigMap-based configuration in Kubernetes

## Development

### Install Dependencies

```bash
npm install
```

This installs:
- React and React DOM
- TypeScript and type definitions
- Vite build tooling
- ESLint for code quality
- React Responsive for mobile optimization

### Run Development Server

```bash
npm run start
```

This starts the Vite dev server with:
- Hot Module Replacement (HMR) for instant updates
- TypeScript compilation
- Source maps for debugging
- Access at `http://localhost:5173/claim`

### Code Quality

Run the linter:

```bash
npm run lint
```

### Development Testing

Test the application locally:

1. Start the dev server: `npm run start`
2. Navigate to `http://localhost:5173/claim/?iid=test-invitation-123`
3. Enter a test OTP (coordinate with GOV API team for test credentials)
4. Verify QR code generation and display

## Building for Production

### 1. TypeScript Compilation

Compile TypeScript to check for type errors:

```bash
npx tsc -b
```

### 2. Build Optimized Bundle

Create production-ready static files:

```bash
npm run build
```

This command:
- Compiles TypeScript to JavaScript
- Bundles React components
- Minifies and optimizes code
- Generates source maps
- Outputs to `dist/` directory
- Replaces environment variable references with placeholders for runtime replacement

The build output structure:
```
dist/
├── claim/
│   ├── index.html
│   ├── assets/
│   │   ├── index-<hash>.js
│   │   ├── index-<hash>.css
│   │   └── ...
│   └── ...
```

### 3. Preview Production Build

Test the production build locally:

```bash
npm run preview
```

Access at `http://localhost:4173/claim`

## Docker Deployment

### Multi-Stage Dockerfile

The [Dockerfile](Dockerfile) uses a multi-stage build process:

**Stage 1: Builder**
- Uses Node.js 22 Alpine image
- Installs dependencies
- Runs production build
- Outputs optimized static files

**Stage 2: Nginx Server**
- Uses Nginx Alpine image
- Copies built files to `/usr/share/nginx/html/claim/`
- Configures Nginx with custom config
- Sets up entrypoint script for runtime environment variable injection

### Building the Docker Image

```bash
npm run docker:build
```

This executes:
```bash
docker build -t harbor.gftinnovation.eu/fame/roa/pubui:1.3 .
```

**Important**: The build includes the source code and compiles it inside the Docker image. No pre-build step is required.

### Pushing to Harbor Registry

```bash
npm run docker:push
```

This executes:
```bash
docker login harbor.gftinnovation.eu
docker push harbor.gftinnovation.eu/fame/roa/pubui:1.3
```

### Running the Docker Container

```bash
docker run -d \
  --name fame-onboarding \
  -p 80:80 \
  -e VITE_CLAIM_API='https://gov.fame-horizon.eu/api/v1.0/invitations' \
  -e VITE_STATUS_API='https://identity.fame-horizon.eu/webapp/credential-offer-status' \
  harbor.gftinnovation.eu/fame/roa/pubui:1.3
```

Access the application at:
- Root redirect: `http://localhost/` → redirects to `/claim/`
- Direct access: `http://localhost/claim/`
- With invitation: `http://localhost/claim/?iid=<invitation-id>`

### Entrypoint Script

The [entrypoint.sh](entrypoint.sh) script runs at container startup and:

1. Reads `VITE_CLAIM_API` and `VITE_STATUS_API` environment variables
2. Finds all `.js` files in `/usr/share/nginx/html`
3. Replaces placeholder strings (`__VITE_CLAIM_API__`, `__VITE_STATUS_API__`) with actual values
4. Starts Nginx to serve the application

This allows the same Docker image to work across different environments with different API endpoints.

### Nginx Configuration

The [nginx.conf](nginx.conf) configures:

- **Port 80** - HTTP listener
- **Root redirects** - `/` redirects to `/claim/`
- **Subpath routing** - All requests under `/claim/` served correctly
- **Client-side routing** - SPA routing handled via `try_files`
- **Relative redirects** - Prevents absolute URL issues

## Kubernetes Deployment

For production deployment on Kubernetes clusters, the application is deployed with ConfigMap-based configuration.

### Kubernetes Resources

The deployment consists of:

- **Deployment** (`roaui`) - Application pod
- **Service** (`roaui`) - Network service (port 80)
- **ConfigMap** (`roaui-config`) - API endpoint configuration
- **Ingress** - External routing rules (defined in `roaui-ingress.yaml`)

### Creating the ConfigMap

Create a ConfigMap with API endpoint configurations:

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: roaui-config
  namespace: fame
data:
  VITE_CLAIM_API: https://gov.fame-horizon.eu/api/v1.0/invitations
  VITE_STATUS_API: https://identity.fame-horizon.eu/webapp/credential-offer-status
```

### Example Deployment Manifest

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: roaui
  namespace: fame
spec:
  replicas: 1
  selector:
    matchLabels:
      app: roa
  template:
    metadata:
      labels:
        app: roa
    spec:
      containers:
      - name: roaui
        image: harbor.gftinnovation.eu/fame/roa/pubui:1.3
        imagePullPolicy: Always
        envFrom:
        - configMapRef:
            name: roaui-config
        ports:
        - containerPort: 80
      imagePullSecrets:
        - name: registrysecret
```

### Deploying to Kubernetes

```bash
# Apply the deployment and service
kubectl apply -f roaui-service.yaml

# Apply ingress rules
kubectl apply -f roaui-ingress.yaml

# Verify deployment
kubectl get deployments -n fame | grep roaui
kubectl get pods -n fame | grep roa

# View logs
kubectl logs -n fame deployment/roaui
```

### Updating the Deployment

```bash
# Build and push new image
npm run docker:build
docker tag harbor.gftinnovation.eu/fame/roa/pubui:1.3 harbor.gftinnovation.eu/fame/roa/pubui:1.4
docker push harbor.gftinnovation.eu/fame/roa/pubui:1.4

# Update deployment
kubectl set image deployment/roaui roaui=harbor.gftinnovation.eu/fame/roa/pubui:1.4 -n fame

# Monitor rollout
kubectl rollout status deployment/roaui -n fame
```

### Accessing the Application

Once deployed, the application is accessible via Ingress:

- **External URL**: `https://roa.fame-horizon.eu/claim/`
- **With Invitation**: `https://roa.fame-horizon.eu/claim/?iid=<invitation-id>`

### Complete Kubernetes Manifests

For complete reference, see the manifest files in the `fame-integration` project:
- [roaui-service.yaml](../fame-integration/roa/roaui-service.yaml) - Deployment, Service, and ConfigMap
- [roaui-ingress.yaml](../fame-integration/roa/roaui-ingress.yaml) - Ingress routing rules

## Deployment Checklist

### For Local Development

1. **Setup**
   - [ ] Install Node.js 22.x
   - [ ] Run `npm install`
   - [ ] Create `.env` file with API endpoints
   - [ ] Verify GOV API and Identity service are accessible

2. **Development**
   - [ ] Start dev server: `npm run start`
   - [ ] Access at `http://localhost:5173/claim`
   - [ ] Test with valid invitation ID
   - [ ] Verify OTP validation works
   - [ ] Check QR code generation
   - [ ] Test responsive design on mobile

3. **Code Quality**
   - [ ] Run linter: `npm run lint`
   - [ ] Fix any TypeScript errors
   - [ ] Test all user flow scenarios

### For Docker Deployment

1. **Build Process**
   - [ ] Ensure latest code is committed
   - [ ] Update version in [package.json](package.json) if needed
   - [ ] Build Docker image: `npm run docker:build`
   - [ ] Test image locally with `docker run`

2. **Registry**
   - [ ] Push to Harbor: `npm run docker:push`
   - [ ] Verify image is accessible in Harbor

3. **Deployment**
   - [ ] Set environment variables (`VITE_CLAIM_API`, `VITE_STATUS_API`)
   - [ ] Run container with correct API endpoints
   - [ ] Verify container starts without errors
   - [ ] Test application accessibility
   - [ ] Verify environment variables were injected (check browser network tab)

4. **Verification**
   - [ ] Navigate to `/claim/` path
   - [ ] Test with real invitation ID
   - [ ] Enter OTP and verify QR code display
   - [ ] Check mobile responsiveness
   - [ ] Verify API calls succeed (browser DevTools)

### For Kubernetes Production Deployment

1. **Build Process**
   - [ ] Build Docker image with version tag
   - [ ] Push to Harbor registry
   - [ ] Update image tag in deployment manifest

2. **Configuration**
   - [ ] Create or update ConfigMap (`roaui-config`)
   - [ ] Set `VITE_CLAIM_API` to GOV API endpoint
   - [ ] Set `VITE_STATUS_API` to Identity service endpoint
   - [ ] Review deployment manifest

3. **Deployment Execution**
   - [ ] Apply ConfigMap: `kubectl apply -f <configmap-file>`
   - [ ] Apply deployment: `kubectl apply -f roaui-service.yaml`
   - [ ] Apply ingress: `kubectl apply -f roaui-ingress.yaml`
   - [ ] Verify pod is running: `kubectl get pods -n fame | grep roa`
   - [ ] Check pod logs for errors: `kubectl logs -n fame deployment/roaui`

4. **Verification**
   - [ ] Access via Ingress URL: `https://roa.fame-horizon.eu/claim/`
   - [ ] Test with real invitation ID from email
   - [ ] Verify OTP validation
   - [ ] Check QR code generation
   - [ ] Scan QR with wallet app and verify credential retrieval
   - [ ] Monitor pod health: `kubectl get pods -n fame -w`
   - [ ] Check for any errors in logs

## User Flow

### Step 1: Email Invitation

User receives an email from the FAME system containing:
- Invitation link: `https://roa.fame-horizon.eu/claim/?iid=abc123-xyz789`
- OTP (One-Time Password): `123456`

### Step 2: OTP Entry

1. User clicks the invitation link
2. Application loads and extracts `iid` from URL
3. User sees OTP entry form
4. User enters the OTP from email
5. Application calls GOV API to validate OTP

### Step 3: QR Code Display

On successful OTP validation:
1. Application receives credential offering URI and pre-auth code from GOV API
2. QR code is generated containing the offering data
3. User sees instructions to scan QR with digital wallet app

### Step 4: Credential Claiming

1. User opens digital wallet app (e.g., compatible W3C VC wallet)
2. User scans the QR code
3. Wallet retrieves and stores the Verifiable Credential
4. Application monitors credential status via Identity service
5. Success message displayed once credential is claimed

### Error Handling

- **Invalid OTP**: User sees remaining attempts count, can retry
- **Expired Invitation**: Error message with contact information
- **Network Errors**: User-friendly error messages
- **Invalid Invitation ID**: Clear error explanation

## Troubleshooting

### Common Issues

**Application Not Loading**
- Check Nginx is running: `docker ps` or `kubectl get pods`
- Verify port mapping is correct (port 80)
- Check browser console for JavaScript errors
- Ensure base path `/claim/` is correctly configured

**API Calls Failing**
- Verify `VITE_CLAIM_API` environment variable is set correctly
- Check `VITE_STATUS_API` environment variable
- Inspect browser Network tab to see actual URLs being called
- Verify GOV API and Identity service are accessible
- Check CORS configuration on API servers

**Environment Variables Not Applied**
- Check entrypoint script logs: `docker logs <container-name>`
- Verify placeholder replacement occurred in built files
- Ensure environment variables are passed to container
- Restart container after updating environment variables

**QR Code Not Displaying**
- Check browser console for errors
- Verify response from GOV API contains `uri` and `preAuthCode`
- Check QR code generation service (api.qrserver.com) is accessible
- Inspect network requests to see if QR image loads

**Responsive Design Issues**
- Test on different screen sizes
- Check React Responsive media queries
- Verify CSS is loading correctly
- Use browser DevTools device emulation

**Docker Build Fails**
- Ensure Node.js 22 image is available
- Check `package.json` for dependency issues
- Verify `npm install` completes successfully
- Review Dockerfile syntax

**Nginx 404 Errors**
- Check nginx.conf `try_files` directive
- Verify files are copied to `/usr/share/nginx/html/claim/`
- Test direct file access vs. client-side routing
- Review nginx error logs

## License

This project is licensed under the terms specified in [LICENSE](LICENSE).

## Project Status

**Production** - This application is actively used in the FAME Marketplace platform for credential onboarding.

## Related Projects

- **GOV API**: Provides the credential claiming endpoint
- **Identity Service**: Handles credential issuance and status tracking
- **FAME Dashboard**: Main user interface for the FAME platform

## Contact

For issues, questions, or contributions, please contact the FAME development team.
