Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
643e7ab
feat: Add Playwright MCP configuration to .gitignore
horner Dec 11, 2025
5b08786
feat: Add embeddable FHIR scheduler widget with slot hold system
horner Dec 11, 2025
61dd891
docs: Add Playwright screenshots for scheduler widget documentation
horner Dec 11, 2025
d7c27e9
todo: Enhance authentication and identity management plan for FHIR sc…
horner Dec 11, 2025
0abe70f
feat: Add visit type selection, questionnaire, and browser history su…
horner Dec 12, 2025
cb4fb36
feat: Auto-scroll to time slots after date selection
horner Dec 12, 2025
4130fef
Add Proxmox deployment workflow with GitHub-hosted runner
MIE-levij Jan 2, 2026
20d86aa
Disabled automatic proxmox deploy
MIE-levij Jan 7, 2026
190e733
Merge branch 'ui-temp'
MIE-levij Jan 7, 2026
393fd11
Added /demo route to serve demo scheduler page
MIE-levij Jan 7, 2026
389bbe7
fix: resolve CORS issues for deployed demo page
MIE-levij Jan 7, 2026
2971e83
fix: correct setState logic in custom zustand store implementation
MIE-levij Jan 7, 2026
0e855dd
Fix timezone issue in slot generation - use local timezone offset ins…
MIE-levij Jan 7, 2026
c2a6f17
fix: serve index.html demo with standalone bundle and inject CSS into…
MIE-levij Jan 7, 2026
1d07b60
fix: build fhir-scheduler bundle in Docker and fix volume mount
MIE-levij Jan 7, 2026
26d3e4b
feat(hl7): accept text/plain and JSON for /hl7/siu endpoint (datasend…
MIE-levij Feb 11, 2026
635a0c6
Fixed swagger docs in deployed version
MIE-levij Feb 13, 2026
365e5a1
Deduplicate HL7 SIU appointments by placer ID
MIE-levij Feb 13, 2026
8010adb
Add index on appointments.identifier and optimize placer ID lookup
MIE-levij Feb 13, 2026
07556fd
feat: add Provider View for viewing booked appointments
MIE-levij Feb 17, 2026
19c359a
feat: add HTTP Basic Auth for all API endpoints and MLLP IP allowlist
MIE-levij Feb 17, 2026
98ecb98
Fixed bug with NPM not terminating correctly on docker compose down. …
MIE-levij Feb 17, 2026
2374e42
Fix: Pass .env file on disk to container.
MIE-levij Feb 17, 2026
baca4cc
Fix: Updated paths and standalone vite build job so new 'Provider vie…
MIE-levij Feb 17, 2026
e1472e0
Fix: Provider view css wasn't included in build
MIE-levij Feb 17, 2026
55f8c04
Store datetimes as naive ISO strings without timezone suffix
MIE-levij Feb 17, 2026
687a695
fix: all appt dates were two days off. removed shiftDate().
MIE-levij Feb 19, 2026
da5d932
feat: provider view now shows appointment type and location for each …
MIE-levij Feb 25, 2026
3ac0fd9
feat: full hl7 message logging + new env variable to set log retentio…
MIE-levij Feb 25, 2026
b3972d2
fix: Appointment types now properly display description instead of co…
MIE-levij Feb 25, 2026
6ddc443
feat: support for parsing appt location from the AIL segment.
MIE-levij Feb 25, 2026
4db4c15
feat: enhance scheduling model documentation with HL7 alignment details
pierzchala-m Mar 20, 2026
07b20a9
style: remove gray gaps between calendar week rows
pierzchala-m Apr 7, 2026
bb012e9
feat: add ImportData component for importing scheduling data from JSO…
pierzchala-m Apr 7, 2026
5101a14
feat: add styles for questionnaire form and buttons
pierzchala-m Apr 7, 2026
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
17 changes: 17 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,22 @@ SQLITE_DB_PATH=./data/fhirtogether.db
# MYSQL_USER=fhir
# MYSQL_PASSWORD=password

# API Authentication (HTTP Basic Auth for all API endpoints)
# If both are set, all non-public endpoints require Basic Auth.
# Public endpoints: /health, /docs/*, /demo, /scheduler/*
# If unset, all endpoints are open (a warning is logged at startup).
# AUTH_USERNAME=admin
# AUTH_PASSWORD=changeme

# HL7 MLLP Socket IP Allowlist
# Comma-separated list of allowed source IPs for the MLLP socket.
# If unset, all IPs are allowed (a warning is logged at startup).
# HL7_MLLP_ALLOWED_IPS=127.0.0.1,10.0.0.5

# HL7 Message Log Retention
# Number of days to keep HL7 message log entries before automatic cleanup.
# Cleanup runs once at startup and then every 24 hours.
# HL7_MESSAGE_LOG_RETENTION_DAYS=7

# Test Mode
ENABLE_TEST_ENDPOINTS=true
33 changes: 31 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,35 @@ src/
- Resource types: Schedule, Slot, Appointment
- Use proper FHIR references (e.g., `Schedule/123`, `Slot/456`)

### Scheduling Model (HL7-Aligned)

- **Resource:** Entity whose time is scheduled (e.g., provider, room, device).
→ FHIR: `Schedule.actor` / `Slot.schedule.actor`
→ HL7 v2: `SCH-1/2` (placer/filler), `AIG/AIS/AIL` (resource groups)

- **Participant:** Any entity involved (e.g., patient, staff, external). Not special-cased.
→ FHIR: `Appointment.participant`
→ HL7 v2: `PID` (patient), `AIP` (personnel), `AIG/AIL/AIS` (resources)

- **Appointment:** **Committed time block** for one or more Resources, with optional Participants. Prevents conflicts; supports multiple participants (like a calendar event).
→ FHIR: `Appointment`
→ HL7 v2: `SCH` (schedule activity), `SIU^S12` (booking), `SIU^S13–S15` (updates/cancel)

- **Schedule:** Defines **availability** for a Resource via recurrence + exceptions. Not booked time.
→ FHIR: `Schedule` + `Slot`
→ HL7 v2: **No standard HL7 v2 message type exists for availability/schedules** — must be managed application-side

- **Rule:** Schedules create availability; Appointments consume it. Resources own time; Participants join it.

#### HL7 v2 Support Summary
| Concept | HL7 v2 Supported? | Implementation |
|---------|-------------------|----------------|
| **Appointments** (booked time) | ✅ Yes | SIU messages: S12 (book), S13 (reschedule), S15 (cancel), S17 (delete), S23 (block) |
| **Schedules** (availability) | ❌ No | No HL7 v2 standard; configure via webchart's scheduling system |

> **FAQ:** "Is HL7 scheduling supported?" → HL7 v2 supports **appointments** (SIU^S12, etc.), but NOT **schedules/availability**. These are different concepts.


### 🎯 DRY (Don't Repeat Yourself)
- **Never duplicate code**: If you find yourself copying code, extract it into a reusable function
- **Single source of truth**: Each piece of knowledge should have one authoritative representation
Expand Down Expand Up @@ -113,7 +142,7 @@ src/
- **Always use Mermaid diagrams** instead of ASCII art for workflow diagrams, architecture diagrams, and flowcharts
- **Use memorable names** instead of single letters in diagrams (e.g., `Engine`, `Auth`, `Server` instead of `A`, `B`, `C`)
- Use appropriate Mermaid diagram types:
- `graph TB` or `graph LR` for workflow architectures
- `graph TB` or `graph LR` for workflow architectures
- `flowchart TD` for process flows
- `sequenceDiagram` for API interactions
- `gitgraph` for branch/release strategies
Expand Down Expand Up @@ -180,4 +209,4 @@ src/
- `PORT` - Server port (default: 4010)
- `STORE_BACKEND` - Database backend (default: sqlite)
- `SQLITE_DB_PATH` - Database file location
- `ENABLE_TEST_ENDPOINTS` - Enable DELETE operations
- `ENABLE_TEST_ENDPOINTS` - Enable DELETE operations
40 changes: 40 additions & 0 deletions .github/workflows/proxmox-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Proxmox Deployment

# on:
# push: # Updates existing containers
# create: # Creates containers for new branches
# delete: # Removes containers when branches are deleted
on:
workflow_dispatch

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy FHIRTogether to Proxmox
uses: maxklema/proxmox-launchpad@main
with:
# Required Proxmox credentials
proxmox_username: ${{ secrets.PROXMOX_USERNAME }}
proxmox_password: ${{ secrets.PROXMOX_PASSWORD }}

# Container configuration
http_port: 4010
linux_distribution: debian

# Optional: SSH access (recommended for security)
public_key: ${{ secrets.SSH_PUBLIC_KEY }}

# Application deployment configuration
project_root: "" # Repository root
install_command: "npm install"
build_command: "npm run build"
start_command: "PORT=4010 npm start"
runtime_language: "nodejs"

# Environment variables for the application
container_env_vars: '{"NODE_ENV": "production", "PORT": "4010"}'

# Services required by FHIRTogether
# SQLite is file-based, so no external database service needed
services: '[]'
12 changes: 11 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,14 +143,21 @@ vite.config.ts.timestamp-*
# ===========================================

# SQLite database files
data/
data/*.db
data/*.db-journal
data/*.db-wal
data/*.db-shm
*.db
*.db-journal
*.db-wal
*.db-shm
*.sqlite
*.sqlite3

# Keep seed data for consistent test data
!data/seed-metadata.json
!data/seed-*.jsonl

# Compiled TypeScript output
dist/

Expand All @@ -168,6 +175,9 @@ dist/
test-results/
playwright-report/

# Playwright MCP configuration
.playwright-mcp/

# Temporary files
tmp/
temp/
Expand Down
21 changes: 19 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,32 @@ FROM node:18
# Set the working directory
WORKDIR /app

# Copy package.json and package-lock.json
# Copy package.json and package-lock.json for root
COPY package.json package-lock.json ./

# Install dependencies
# Copy package.json for fhir-scheduler
COPY packages/fhir-scheduler/package.json packages/fhir-scheduler/package-lock.json* ./packages/fhir-scheduler/

# Install dependencies for root
RUN npm install

# Install dependencies for fhir-scheduler
WORKDIR /app/packages/fhir-scheduler
RUN npm install

# Go back to root
WORKDIR /app

# Copy the rest of the application code
COPY . .

# Build the fhir-scheduler standalone bundle
WORKDIR /app/packages/fhir-scheduler
RUN npm run build:standalone

# Go back to root
WORKDIR /app

# Expose the application port
EXPOSE 4010

Expand Down
74 changes: 53 additions & 21 deletions Embeddable_FHIR_Scheduler.md
Original file line number Diff line number Diff line change
Expand Up @@ -498,30 +498,62 @@ function BookingForm({ questionnaireFormData, onQuestionnaireChange }) {

## 🛠️ Implementation Phases

### Phase 1: Core Infrastructure
- [ ] Create package structure with Vite + TypeScript
- [ ] Implement Zustand store with basic state
- [ ] Create FHIR API client
- [ ] Build ProviderList component

### Phase 2: Slot Selection
- [ ] Build SlotCalendar with date picker
- [ ] Implement slot hold API endpoints on server
- [ ] Add hold/release logic to store
- [ ] Display hold countdown timer

### Phase 3: Booking Flow
- [ ] Create BookingForm with patient info fields
- [ ] Integrate QuestionnaireRenderer
- [ ] Implement booking submission with hold validation
- [ ] Build Confirmation component

### Phase 4: Polish & Distribution
- [ ] Create standalone Web Component bundle
- [ ] Add Tailwind styles with CSS purging
### Phase 1: Core Infrastructure ✅
- [x] Create package structure with Vite + TypeScript
- [x] Implement Zustand store with basic state
- [x] Create FHIR API client
- [x] Build ProviderList component

### Phase 2: Slot Selection ✅
- [x] Build SlotCalendar with date picker
- [x] Implement slot hold API endpoints on server
- [x] Add hold/release logic to store
- [x] Display hold countdown timer

### Phase 3: Booking Flow ✅
- [x] Create BookingForm with patient info fields
- [x] Integrate QuestionnaireRenderer using https://github.com/mieweb/questionnaire-builder/
- [x] Implement booking submission with hold validation
- [x] Build Confirmation component

### Phase 4: Polish & Distribution 🔄
- [x] Create standalone Web Component bundle
- [x] Add styles (plain CSS for compatibility)
- [x] Add Playwright E2E test scaffolding
- [ ] Write comprehensive tests
- [ ] Publish to npm

### Phase 5: Identity Management

> 📖 See [AUTH.md](packages/fhir-scheduler/docs/AUTH.md) for complete documentation.

- [ ] **Auth mode support** — `anonymous`, `token`, `callback`, `smart` modes
- [ ] **Token injection**
- [ ] Accept static `accessToken` prop
- [ ] Support `getAccessToken()` async refresh function
- [ ] Auto-attach Bearer token to FHIR requests
- [ ] **Anonymous booking with verification**
- [ ] Email verification flow (send/verify callbacks)
- [ ] SMS verification via host-provided gateway
- [ ] Optional CAPTCHA integration
- [ ] **Identity callback pattern**
- [ ] `onIdentityRequired` hook for host-controlled auth
- [ ] Support OAuth popup/redirect flows
- [ ] Handle auth cancellation gracefully
- [ ] **Pre-populated user info**
- [ ] Accept `user` prop with known identity
- [ ] Skip patient info fields if verified user provided
- [ ] Link to existing FHIR Patient resource
- [ ] **SMART on FHIR launch** (optional)
- [ ] EHR launch context support
- [ ] Standalone launch with authorization
- [ ] Token refresh handling
- [ ] **Post-booking notifications**
- [ ] Calendar integration (Google Calendar, Outlook) via email
- [ ] SMS reminders via host-provided gateway
- [ ] Cancelation/reschedule links


---

## 📄 License
Expand Down
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,43 @@ STORE_BACKEND=postgres
Send HL7v2 scheduling messages (e.g., `SIU^S12`, `S13`, `S15`) to:

```
POST /$hl7v2-ingest
POST /hl7/siu
```

**Option 1: Raw HL7 text** (Content-Type: `text/plain` or `x-application/hl7-v2+er7`)
```
MSH|^~\&|LEGACY_EHR|MAIN_HOSPITAL|FHIRTOGETHER|SCHEDULING_GATEWAY|20231209120000||SIU^S12|12345|P|2.3
SCH|10001^10001|10001^10001|||10001|OFFICE^Office visit|reason|OFFICE|30|m|...
PID|1||42||DOE^JOHN||19800101|M|||123 Main St^^City^ST^12345||5551234567
...
```

**Option 2: JSON wrapper** (Content-Type: `application/json`)
```json
{
"message": "MSH|^~\\&|SCHED|...<raw HL7v2>",
"sourceSystem": "LegacyScheduler"
"wrapMLLP": false
}
```

**Response for text requests:** Raw HL7 ACK message
```
MSH|^~\&|FHIRTOGETHER|SCHEDULING_GATEWAY|LEGACY_EHR|MAIN_HOSPITAL|20231209120001||ACK^S12|ACK12345|P|2.3
MSA|AA|12345|Message processed successfully
```

**Response for JSON requests:** JSON-wrapped ACK
```json
{
"message": "MSH|^~\\&|FHIRTOGETHER|SCHEDULING_GATEWAY|...\rMSA|AA|12345|Message processed successfully|||"
}
```

**ACK Codes:**
- `AA` (Application Accepted) - Message processed successfully
- `AR` (Application Rejected) - Message format error, do not retry
- `AE` (Application Error) - Processing failure, can retry

The server parses the message and converts it into FHIR `Slot` and `Schedule` resources internally.

## 📦 API Endpoints
Expand All @@ -135,7 +162,7 @@ FHIR-compliant endpoints (all responses follow FHIR Bundles or resource schemas)
| GET | `/Schedule` | Retrieve provider availability |
| POST | `/Schedule` | Define provider planning horizon |
| POST | `/Appointment` | Book an appointment |
| POST | `/$hl7v2-ingest` | Ingest HL7v2 scheduling msg |
| POST | `/hl7/siu` | Ingest HL7v2 SIU message (text or JSON) |

## 🧪 Test Server Mode

Expand Down
Loading