Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ The internal staff-facing Vue 2 application. Staff use it for queue management,

The public-facing Vue 2 application for booking appointments, viewing booked appointments, managing account settings, handling sign-in flows, and viewing walk-in queue status.

### `appointment-booking`

The new under development public-facing React 19 + TypeScript application for booking appointments at Service BC locations. It will eventually replace `appointment-frontend`. See [`appointment-booking/README.md`](./appointment-booking/README.md) for setup instructions.

### `notifications-api`

A separate Flask service for outbound notifications. It exposes authenticated `POST /api/v1/notifications/sms` and `POST /api/v1/notifications/email` endpoints and supports pluggable delivery providers, including GC Notify, CHES, and logging/custom implementations.
Expand All @@ -41,7 +45,7 @@ This legacy Flask service accepts feedback submissions and forwards them to the
## Technology Stack

- Backend: Python, Flask, Flask-RESTX, SQLAlchemy, Flask-Migrate, Flask-SocketIO, Marshmallow, Gunicorn, and Gevent.
- Frontend: Vue 2, TypeScript, Vue Router, Vuex, Vuetify, BootstrapVue, Buefy, and Axios.
- Frontend: Vue 2 , React 19 + TypeScript + Vite , Vue Router, Vuex, Vuetify, BootstrapVue, Buefy, and Axios.
- Data and integrations: PostgreSQL, Redis-backed real-time/message queue usage, MinIO for object storage, Keycloak/OIDC authentication, optional Snowplow analytics, and GC Notify/CHES/custom notification providers.
- Serving/runtime: Nginx serves built frontend assets in containerized deployments.

Expand Down Expand Up @@ -77,6 +81,7 @@ It also provisions project-local Python environments for both `api` and `notific

- `5000`: queue management API
- `5002`: notifications API
- `5173`: appointment booking frontend (React)
- `8080`: staff frontend
- `8081`: appointment frontend
- `8085`: Keycloak auth server
Expand Down Expand Up @@ -112,6 +117,9 @@ The devcontainer installs dependencies automatically for `api`, `notifications-a

cd ./appointment-frontend
npm install

cd ./appointment-booking
npm install
```

3. Create the required local config files:
Expand Down Expand Up @@ -188,6 +196,15 @@ cd ./appointment-frontend
npm run serve -- --port 8081
```

Appointment booking frontend (React):

```bash
cd ./appointment-booking
npm run dev
```

Opens at `http://localhost:5173`. See [`appointment-booking/README.md`](./appointment-booking/README.md) for Docker and environment variable details.

#### Local Config Files

These are the main local files you should expect to have in place when running the application locally:
Expand Down
2 changes: 1 addition & 1 deletion api/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class LocalConfig(BaseConfig):

SERVER_NAME = None
SESSION_COOKIE_DOMAIN = None
CORS_ALLOWED_ORIGINS = ["http://localhost:8080", "http://localhost:8081"]
CORS_ALLOWED_ORIGINS = ["http://localhost:8080", "http://localhost:8081", "http://localhost:5173", "http://localhost:5174"]
SQLALCHEMY_ECHO = False
SECRET_KEY = "pancakes"

Expand Down
1 change: 1 addition & 0 deletions appointment-booking/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
*.local
ci_output.txt

# Editor directories and files
.vscode/*
Expand Down
34 changes: 6 additions & 28 deletions appointment-booking/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,42 +1,20 @@
# Multi-stage build for appointment-booking SSR app
# Stage 1: Builder
FROM node:22-alpine AS builder

WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy source
COPY . .

# Build client and server
RUN npm run build

# Stage 2: Runtime
FROM node:22-alpine

WORKDIR /app

# Install only production dependencies
COPY package*.json ./
RUN npm ci --omit=dev

# Copy built artifacts from builder
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/server.js ./server.js
COPY --from=builder /app/public ./public

# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e "require('http').get('http://localhost:5173/config/runtime-config.json', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})" || exit 1
FROM nginx:1.25.3

# Expose port
EXPOSE 5173
COPY nginx.conf /etc/nginx/nginx.conf
RUN mkdir /app
COPY --from=builder /app/dist /app

# Run production server
ENV NODE_ENV=production
CMD ["node", "server.js"]
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]
88 changes: 18 additions & 70 deletions appointment-booking/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Appointment Booking App

A React 19 + TypeScript appointment booking interface built for BC Gov, featuring server-side rendering (SSR), automated tests, and OpenShift deployment readiness.
A React 19 + TypeScript appointment booking interface built for BC Gov.

## Prerequisites

Expand All @@ -21,128 +21,76 @@ npm install
### Start the frontend

```bash
# Default: proxies /api/v1 to http://localhost:5000
# Proxies /api/v1 to http://localhost:5000 (default)
npm run dev

# If your local API runs on a different port (e.g. 5100 on macOS where port 5000 is reserved by mDNS):
# If your local API runs on port 5100
npm run dev:local
```

Open `http://localhost:5173`. If that port is in use, the server automatically falls back to `5174`.

### Frontend-only (without backend)

The frontend starts without a running backend. API calls to `/api/v1/*` will fail gracefully until the backend is running.
Open `http://localhost:5173`. The frontend starts without a running backend — API calls will fail gracefully until the backend is running.

### Run Checks

```bash
# Run everything at once (lint + type-check + test + build + license)
npm run ci:check

# Optional: run checks individually
# Individually
npm run test
npm run test:watch
npm run lint
npm run type-check
npm run license-check
```

### Build for Production

```bash
# Compile TypeScript, build client & server
npm run build

# Output in ./dist/
# - dist/client/ (browser assets)
# - dist/server/ (SSR entry point)

# Run production build locally
NODE_ENV=production node server.js
# Opens at http://localhost:5173
```

---

## Docker

### Build Image

```bash
docker build -t appointment-booking:latest .
```
### Run with Docker Compose (recommended)

### Run Container
From the repository root:

```bash
docker run -p 5173:5173 \
-e API_BASE_URL=http://api-service/api/v1 \
-e NODE_ENV=production \
appointment-booking:latest
docker compose --profile api up --build
```

**Note:** Use `host.docker.internal` on macOS/Windows to access localhost from container. On Linux, use the Docker network or actual IP.
Starts the frontend, API, database, and Keycloak. Open `http://localhost:5173`.

> Note: running Docker compose will occupy port 5173. If you then run `npm run dev`, Vite will fall back to port 5174.

### Run with Docker Compose
### Build and run manually

```bash
docker-compose up appointment-booking
docker build -t appointment-booking .
docker run -p 5173:8080 appointment-booking
```

(Assumes compose.yaml exists with service definition)

---

## Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `NODE_ENV` | `development` | Controls production vs development mode |
| `PORT` | `5173` | Server listen port |
| `FALLBACK_PORT` | `PORT + 1` | Fallback port when `PORT` is already in use (dev) |
| `API_PROXY_TARGET` | `http://localhost:5000` | Backend URL for Vite's `/api/v1` dev proxy (**dev only**) |
| `API_BASE_URL` | `/api/v1` | API base URL served to the client via runtime config (**production**) |
| `REQUEST_TIMEOUT_MS` | `10000` | API request timeout in milliseconds |
## Runtime Config

### Runtime Config
The app fetches `/config/runtime-config.json` on startup to get its configuration. The default file is at [`public/config/runtime-config.json`](./public/config/runtime-config.json) and is served as a static file by nginx.

The app loads configuration from `/config/runtime-config.json` endpoint (served by `server.js`):
In OpenShift, a ConfigMap mounts over this file to provide environment-specific values without rebuilding the image.

```json
{
"apiBaseUrl": "/api/v1",
"apiBaseUrl": "http://localhost:5000/api/v1",
"requestTimeoutMs": 10000
}
```

This endpoint reads from environment variables at startup, allowing Kubernetes ConfigMaps/Secrets to drive configuration without rebuilding the image.

---

## License

MIT + Apache-2.0 (for BC Gov design system components)

See `LICENSE` file and run `npm run license-check` to verify all dependencies comply.

---

## Contributing

1. Create a feature branch
2. Make changes and run tests: `npm run test`
3. Lint and format: `npm run lint && npm run format`
4. Push and open a PR
5. GitHub Actions runs quality checks automatically
6. Merge once checks pass

---

## Links

- **BC Gov Design System:** https://github.com/bcgov/design-system
- **React Docs:** https://react.dev
- **Vite Docs:** https://vite.dev
- **TypeScript Docs:** https://www.typescriptlang.org
- **OpenShift Docs:** https://docs.openshift.com
Loading
Loading