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.

FeatureDescription
Production URLservice.progressivesurface.comLive (2026-06-29)
API Backendhttps://api.progressivesurface.com/api/service
AuthenticationAzure AD / Entra (via @progressivesurface/auth); SPA app reg 34d0df1c-57b1-455f-a7fe-782361d714e0
DeploymentAzure App Service psi-service (Linux/Node 24, PS-WEBAPPS) + Private Endpoint 10.160.140.24, publicNetworkAccess=Disabled
RepositoryC:\GIT\psi-serviceProgressiveSurface/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.DATE overrides 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.1287 endpoints).
  • Phase 4 — reports (service/contact/activity), Excel export, email notifications.
  • Activity-log grid (ACTIVITY.LOG.1287); adopt @progressivesurface/ui CSS once on Tailwind v4.

Architecture

psi-service (React SPA) ──Entra access token (API scope)──▶ PSI.UniData.API ──U2──▶ UniData SERVICE.1287
ComponentTechnology
FrameworkReact 18 + TypeScript
BuildVite 5
StylingTailwindCSS 3 (brand green #027A54 via config)
Server stateTanStack React Query
Client stateZustand (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/auth encodes).
  • Requests an access token for the PSI.UniData.API scope (api://b3db69d9-…/user_impersonation); the API validates the token audience.
  • VITE_USE_DEV_ENDPOINTS=true bypasses auth and uses the API’s anonymous /dev routes 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, delegated user_impersonation on 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); public service A → .24 + asuid.service TXT. DC privatelink records (psi-service / .scm.24) added on PS-AZ-DC01.
  • CORS: api.progressivesurface.com must allowlist https://service.progressivesurface.com (+ https://psi-service.azurewebsites.net). Note deploy-ps-proxy.yml preserves the live appsettings.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.