Rebrand: rename ondeck->horizon in package.json, docker-compose, SERVER.md, docs; production stack at /opt/stacks/horizon

This commit is contained in:
lorentz 2026-04-12 23:59:20 +00:00
parent 77f23e12cd
commit bbdbe1ddc0
8 changed files with 464 additions and 46 deletions

View file

@ -17,22 +17,39 @@ tail -f /var/log/horizon.log
```
## Details
- **Port:** 3000 (proxied via Pangolin/Nginx to https://horizon.seubert.cloud)
- **Working directory:** `/opt/projects/OnDeck/ondeck`
- **Mode:** `npm run dev` (Next.js with Turbopack)
- **Service file:** `/etc/systemd/system/horizon.service`
- **Port:** 3000 (proxied via Pangolin to https://horizon.seubert.cloud)
- **Stack directory:** `/opt/stacks/horizon`
- **Mode:** Docker Compose (production Next.js standalone build)
- **Container names:** `horizon-app`, `horizon-db`
## After code changes
Changes hot-reload automatically. If the server gets into a bad state:
## Managing the container stack
```bash
systemctl restart horizon
cd /opt/stacks/horizon
docker compose pull && docker compose up -d --build # rebuild & restart
docker compose restart horizon-app # restart app only
docker compose down # stop all
docker compose logs -f horizon-app # app logs
docker compose logs -f horizon-db # db logs
```
## After code changes (rebuild image)
```bash
cd /opt/stacks/horizon
docker compose up -d --build horizon-app
```
## After schema changes
Run these before restarting:
```bash
cd /opt/projects/OnDeck/ondeck
npx prisma db push
npx prisma generate
systemctl restart horizon
cd /opt/stacks/horizon
docker compose exec horizon-app npx prisma db push
docker compose restart horizon-app
```
## Dev server (local development only)
The systemd dev service still runs at `/opt/projects/OnDeck/ondeck`:
```bash
systemctl start horizon # start dev
systemctl stop horizon # stop dev
systemctl restart horizon # restart dev
systemctl status horizon # check status
```

View file

@ -1,16 +1,17 @@
version: '3.8'
services:
app:
horizon-app:
build:
context: .
dockerfile: Dockerfile
container_name: horizon-app
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://ondeck_user:ondeck_password_2026!@db:5432/ondeck
- NEXTAUTH_URL=http://localhost:3000
- DATABASE_URL=postgresql://horizon_user:${DB_PASSWORD}@horizon-db:5432/horizon
- NEXTAUTH_URL=${NEXTAUTH_URL}
- NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
- AZURE_AD_CLIENT_ID=${AZURE_AD_CLIENT_ID}
- AZURE_AD_CLIENT_SECRET=${AZURE_AD_CLIENT_SECRET}
@ -19,35 +20,43 @@ services:
- AFW_DATABASE=${AFW_DATABASE}
- AFW_USER=${AFW_USER}
- AFW_PASSWORD=${AFW_PASSWORD}
- CRON_SECRET=${CRON_SECRET}
- METRICS_SECRET=${METRICS_SECRET}
- GRAPH_CLIENT_ID=${GRAPH_CLIENT_ID}
- GRAPH_CLIENT_SECRET=${GRAPH_CLIENT_SECRET}
- GRAPH_TENANT_ID=${GRAPH_TENANT_ID}
depends_on:
- db
horizon-db:
condition: service_healthy
restart: unless-stopped
networks:
- ondeck-network
- horizon-internal
- pangolin
db:
horizon-db:
image: postgres:17
container_name: horizon-db
environment:
- POSTGRES_DB=ondeck
- POSTGRES_USER=ondeck_user
- POSTGRES_PASSWORD=ondeck_password_2026!
ports:
- "5432:5432"
- POSTGRES_DB=horizon
- POSTGRES_USER=horizon_user
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- ondeck-pgdata:/var/lib/postgresql/data
- horizon-pgdata:/var/lib/postgresql/data
restart: unless-stopped
networks:
- ondeck-network
- horizon-internal
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ondeck_user -d ondeck"]
test: ["CMD-SHELL", "pg_isready -U horizon_user -d horizon"]
interval: 10s
timeout: 5s
retries: 5
volumes:
ondeck-pgdata:
horizon-pgdata:
driver: local
networks:
ondeck-network:
horizon-internal:
driver: bridge
pangolin:
external: true

View file

@ -6,11 +6,11 @@ The Designation Admin Interface allows administrators to create and manage clien
## Key Concepts
### Designations are Created in OnDeck
### Designations are Created in Horizon
**All designations are created and managed within the OnDeck application.** They are NOT imported from AFW. Instead:
**All designations are created and managed within the Horizon application.** They are NOT imported from AFW. Instead:
1. **Create a designation** in OnDeck (e.g., "Shape", "Premium", "VIP")
1. **Create a designation** in Horizon (e.g., "Shape", "Premium", "VIP")
2. **Optionally map it to an AFW ANotId** (a GUID that identifies customers in AMS360)
3. **Sync to automatically assign** clients based on their AFW data
@ -19,12 +19,12 @@ The Designation Admin Interface allows administrators to create and manage clien
Each designation can have an **optional AFW ANotId** field:
- This is a GUID from AMS360/AFW (e.g., `13CF7DCB-F6AF-42C2-A7AB-26641A216A81`)
- It identifies a group of customers in the AFW database
- When you sync, clients with this ANotId in AFW get assigned to this designation in OnDeck
- When you sync, clients with this ANotId in AFW get assigned to this designation in Horizon
### Example Workflow
```
1. Admin creates "Shape" designation in OnDeck
1. Admin creates "Shape" designation in Horizon
- Name: Shape
- Color: Indigo
- AFW ANotId: 13CF7DCB-F6AF-42C2-A7AB-26641A216A81
@ -35,7 +35,7 @@ Each designation can have an **optional AFW ANotId** field:
3. Admin saves the designation
4. Admin clicks "Sync" on the Shape designation
- OnDeck queries AFW for customers with ANotId 13CF7DCB-...
- Horizon queries AFW for customers with ANotId 13CF7DCB-...
- Finds matching clients in local database
- Updates their designationId to "Shape"
- Result: 145 clients updated, 5 skipped (already had Shape)

View file

@ -9,14 +9,14 @@
### What this script does
This script imports historical SHAPE task completion data from the team's SharePoint Excel files into OnDeck. It is a **one-time migration tool** — not part of the ongoing sync.
This script imports historical SHAPE task completion data from the team's SharePoint Excel files into Horizon. It is a **one-time migration tool** — not part of the ongoing sync.
Each team member has a folder on SharePoint at:
```
Claims/SHAPE Accounts/{MEMBER_FOLDER}/{Client}.xlsx
```
The script reads every client Excel file for each advocate, matches it to a client in OnDeck, and records whether each SHAPE task was completed, marked N/A, or left blank.
The script reads every client Excel file for each advocate, matches it to a client in Horizon, and records whether each SHAPE task was completed, marked N/A, or left blank.
### Team member folders
@ -52,12 +52,12 @@ After running, the report shows:
| Stat | Meaning |
|---|---|
| **Files processed** | Excel files successfully downloaded and parsed |
| **Clients matched** | Clients in the Excel that were found in OnDeck |
| **Clients matched** | Clients in the Excel that were found in Horizon |
| **Clients unmatched** | Clients that could not be matched — need manual review |
| **Fuzzy matches** | Clients matched by approximate name — review for correctness |
| **Tasks updated** | Existing tasks whose status was set to Completed or N/A |
| **Tasks assigned** | Tasks that had the advocate linked to them |
| **Tasks created** | Tasks from the Excel that didn't exist in OnDeck yet |
| **Tasks created** | Tasks from the Excel that didn't exist in Horizon yet |
| **Duplicates deleted** | Extra copies of the same task removed from the DB |
| **Ad-hoc created** | "Additional Services" rows and unmatched tasks saved as free-form tasks |
| **Advocates assigned** | Clients that had their Claims Advocate set from this import |
@ -65,7 +65,7 @@ After running, the report shows:
### What gets skipped
The script will **not** import a row if:
- The client name in the Excel cannot be matched to any OnDeck client (logged as "unmatched")
- The client name in the Excel cannot be matched to any Horizon client (logged as "unmatched")
- The file is named `archive*`, contains `template`, `master workbook`, or starts with `all shape` — these are intentionally excluded
Nothing else is filtered. Every task row in every matched client file is processed.
@ -80,9 +80,9 @@ Specifically: if the database already has two identical tasks for the same clien
After running the dry run, check the **"Unmatched Clients"** section of the report. For each one:
1. Check if the client exists in OnDeck under a different spelling
2. If yes — either rename the client in OnDeck to match, or rename the Excel file
3. If no — create the client in OnDeck first, then re-run the import
1. Check if the client exists in Horizon under a different spelling
2. If yes — either rename the client in Horizon to match, or rename the Excel file
3. If no — create the client in Horizon first, then re-run the import
---
@ -116,7 +116,7 @@ Each .xlsx file:
4. processFile() × N For each file:
└── parseExcelFile() Parse XLSX without external libraries (custom ZIP+XML)
└── parseExcelBlocks() Extract client blocks and task rows
└── matchClient() Match Excel client name → OnDeck client
└── matchClient() Match Excel client name → Horizon client
└── processTaskRow() × N For each task row:
└── findMatchingTasks() Look up existing task in DB (±5-day window)
└── deduplication Delete extras if >1 match

View file

@ -1,5 +1,5 @@
{
"name": "ondeck",
"name": "horizon",
"version": "0.1.0",
"private": true,
"scripts": {

View file

@ -1,7 +1,7 @@
/**
* SHAPE Historical Data Import Script
*
* Imports historical task completion data from SharePoint Excel files into OnDeck.
* Imports historical task completion data from SharePoint Excel files into Horizon.
* Each team member has a folder in Claims/SHAPE Accounts/{MEMBER_FOLDER}/{Client}.xlsx
*
* Usage: