Customer Service Manager (psi-service)
Web application for triaging and tracking customer service tickets, replacing the failing legacy VB6 PTIServiceTicket desktop app.
Overview
psi-service is a React/TypeScript single-page application that gives PSI service staff a modern interface over the customer service ticket data in UniData (SERVICE.1287). It connects to the PSI.UniData.API for data access, the same pattern as Redbook Web and PSI Dispatch.
It replaces the VB6 PTIServiceTicket app (C:\GIT\PTI_VBP\PTIServiceTicket_2.0). The full behavioral accounting of the legacy app lives in C:\GIT\Service5\docs\migrations\SERVICE_TICKET_MIGRATION_SPEC.md.
| Feature | Description |
|---|---|
| Production URL | service.progressivesurface.com — Live (2026-06-29) |
| API Backend | https://api.progressivesurface.com/api/service |
| Authentication | Azure AD / Entra (via @progressivesurface/auth); SPA app reg 34d0df1c-57b1-455f-a7fe-782361d714e0 |
| Deployment | Azure App Service psi-service (Linux/Node 24, PS-WEBAPPS) + Private Endpoint 10.160.140.24, publicNetworkAccess=Disabled |
| Repository | C:\GIT\psi-service — ProgressiveSurface/psi-service |
Features
Phase 1 — read-only triage board ✅ (deployed 2026-06-29)
- SLA priority board: color-coded, sortable ticket grid (priority, ticket #, project, service type, account, problem, created, days open, assigned to, status). Row tint and priority chip by SLA band.
- Time-decay priority: a faithful client-side port of the VB6 algorithm — priority decays toward 0 (more urgent) as an open ticket ages within its SLA window. Completed/installation = 10; customer-possession (POS.ARROW = C) does not decay;
HARD.DATEoverrides the band window; past-SLA collapses to ~1; safety flag ×0.1. Computed in the browser so the board re-sorts/re-colors live (replaces the VB6 60-second refresh timer with React Query auto-refresh).- Bands: red 0–1.9, orange 2–3.9, yellow 4–6.9, blue 7–9, green (completed), white (installation).
- Filters: status (open/completed/both), account, assigned-to, project — applied server-side; free-text search applied client-side over problem/account/contact/etc.
- Read-only detail pane: problem/solution/notes, priority factors (urgency, support level, safety, possession), embedded contact, and multi-value lists (parts, vendors, service types, phones, emails).
Planned (later phases)
- Phase 2 — ticket create/edit (
POST/PUT /api/service) with optimistic concurrency. - Phase 3 — contact management (reuses the existing
CONTACT.1287endpoints). - Phase 4 — reports (service/contact/activity), Excel export, email notifications.
- Activity-log grid (
ACTIVITY.LOG.1287); adopt@progressivesurface/uiCSS once on Tailwind v4.
Architecture
psi-service (React SPA) ──Entra access token (API scope)──▶ PSI.UniData.API ──U2──▶ UniData SERVICE.1287
| Component | Technology |
|---|---|
| Framework | React 18 + TypeScript |
| Build | Vite 5 |
| Styling | TailwindCSS 3 (brand green #027A54 via config) |
| Server state | TanStack React Query |
| Client state | Zustand (filters) |
| Auth | @progressivesurface/auth (MSAL/Entra) from GHE Packages |
Backend (PSI.UniData.API): ServiceEndpoints / ServiceService read SERVICE.1287 via the VB_GETLIST.REV1 + BSELECT pattern (shared with the Dispatch list). The API returns the raw SLA inputs rather than a computed priority, so the board can recompute live. Per-user Entra→UniData session for audit; anonymous service-account /dev/* routes for local development.
Auth profile
Approved client/app-layer auth application under the PSI Web App Compliance Standard:
- SPA auth via Entra/MSAL using redirect flow + localStorage cache (the defaults
@progressivesurface/authencodes). - Requests an access token for the PSI.UniData.API scope (
api://b3db69d9-…/user_impersonation); the API validates the token audience. VITE_USE_DEV_ENDPOINTS=truebypasses auth and uses the API’s anonymous/devroutes for local development only.
Deployment
Production deployment runs through GitHub Actions on the PSI self-hosted runner using identity-based Azure login (az login --identity) — no publish profiles or basic credentials. The workflow authenticates npm to GHE Packages with the job GITHUB_TOKEN (packages: read) to resolve @progressivesurface/*, builds the SPA, and zip-deploys to Azure App Service (PS-WEBAPPS). The health gate accepts 200/401/403 per the intended auth posture.
The Azure App Service and the SPA Entra app registration are provisioned at first deploy; the workflow reads the app name and auth IDs from repo variables.
Go-live (2026-06-29, issue #2)
Provisioned to the compliance standard: App Service psi-service on the shared asp-erp-migration-tool plan (B3 Linux, NODE|24-lts, startup npm start), system-assigned MI, VNet integration on PS-WebApps, private endpoint 10.160.140.24 (PS-ProdData), publicNetworkAccess=Disabled, SCM/FTP basic creds disabled, HTTPS-only, wildcard *.progressivesurface.com cert bound (SNI), connected to shared psi-webapps-insights.
- Entra: SPA app reg Customer Service Manager (
34d0df1c-57b1-455f-a7fe-782361d714e0) — SPA redirect URIs for the custom domain +psi-service.azurewebsites.net, delegateduser_impersonationon PSI.UniData.API, admin-consented; tenant-wide consent for basic sign-in scopes so users sign in silently. - DNS: Azure private zone
psi-service+.scm→.24(PE zone group); publicserviceA →.24+asuid.serviceTXT. DC privatelink records (psi-service/.scm→.24) added on PS-AZ-DC01. - CORS:
api.progressivesurface.commust allowlisthttps://service.progressivesurface.com(+https://psi-service.azurewebsites.net). Notedeploy-ps-proxy.ymlpreserves the liveappsettings.Production.json(merges only new sections), so the live PS-PROXY config is updated in place + service restarted; the repo (deploy/appsettings.Production.json) is the source of truth for fresh installs. - Repo variables:
AZURE_CLIENT_ID,AZURE_TENANT_ID,API_SCOPE,API_URL(https://api.progressivesurface.com/api),AZURE_WEBAPP_NAME(psi-service),HEALTH_URL.
Related
- PSI.UniData.API — data backend (
/api/service) - Redbook Web — sibling UniData-backed SPA
- Web App Compliance Standard
- *)