Redbook Web
Modern web application for managing RFC (Request for Change) quality issue tickets, replacing the legacy WPF Redbook application.
Overview
Correctness implementation checkpoint (2026-09-08, not deployed)
Local implementation now uses API business priority labels (0-High, 1-Normal, 2-Low), preserves calendar dates without timezone shifts, and parses activity boundaries while retaining original text. Coordinated API changes interpret desktop department codes and preserve untouched raw attributes during updates. Deployment validation now includes lint, unit tests, and packaged server/asset checks before upload.
These changes are awaiting live parity and release validation. Shared concurrent editing, missing display context, remaining desktop workflows, and migration to the shared PSI UI/grid packages remain in the active plan: C:\git\redbook-web\docs\plans\2026-09-08-redbook-correctness-parity-ui.md. Local tests do not establish cross-client write safety or production acceptance.
Redbook Web is a React/TypeScript single-page application that provides a modern interface for creating, editing, and tracking quality issues (RFCs). It connects to the PSI.UniData.API for data access to the UniData REDBOOK.1287 table.
| Feature | Description |
|---|---|
| Production URL | https://redbook.progressivesurface.com |
| API Backend | https://api.progressivesurface.com/api/redbook |
| Authentication | Azure AD (PSI credentials) |
| Deployment | Azure App Service (Linux) + Private Endpoint |
| Repository | C:\GIT\redbook-web |
Features
Current (Phase 1) ✅
- RFC CRUD: Create, read, update, delete quality issues
- Search: Filter by project, status, date range, department, text
- Department Status Grid: Track which departments have addressed an issue — names loaded live from UniData at runtime; grid layout and MV slot order match ProApp XAML exactly
- Corrective Action Fields: Audit cause (free-text), containment, preventative actions
- Project Search: Search-as-you-type combobox with 300ms debounce; calls
/api/redbook/lookups/projects?q= - ECN/NCN Display: View linked engineering changes and non-conformances
- Lookups from UniData: Employees, accounts, cost centers, audit types, departments (with slot index)
Planned (Phase 2-4)
- ECN/NCN linking UI (add/remove links)
- Copy RFC to multiple projects
- Excel export
- PDF reports
- QR code generation
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ REDBOOK WEB FRONTEND │
│ React + TypeScript + TailwindCSS + React Query │
│ Azure Static Web Apps │
└─────────────────────────────────────────────────────────────────┘
│
▼ HTTPS + JWT
┌─────────────────────────────────────────────────────────────────┐
│ PSI.UNIDATA.API │
│ /api/redbook/* endpoints │
│ Windows Service on PS-PROXY │
└─────────────────────────────────────────────────────────────────┘
│
▼ U2 Toolkit
┌─────────────────────────────────────────────────────────────────┐
│ UNIDATA (MRP-PROD) │
│ REDBOOK.1287 table │
└─────────────────────────────────────────────────────────────────┘
UniData Field Mapping
The API maps these UniData fields from REDBOOK.1287. Note: These position numbers reflect the API’s internal mapping and may differ from the raw UniData field positions documented in Redbook Data Dictionary.
| Field | UniData Position | Description |
|---|---|---|
| RFC.NO | Key | Unique identifier |
| JOB.NO | 1 | Project number |
| PROBLEM | 2 | Problem description |
| DRAWING.NO | 3 | Related drawing |
| PART.NO | 4 | Related part number |
| ENTERED.BY | 6 | Creator initials |
| ENTERED.DATE | 7 | Creation date |
| PROBLEM.TYPE | 8 | Problem category code |
| ROOT.CAUSE | 9 | Root cause code |
| SOLUTION | 10 | Solution text |
| PRIORITY | 12 | Priority (1-4) |
| STATUS | 13 | Open/Closed |
| AUDIT.TYPE | 21 | Audit category |
| AUDIT.CAUSE | 22 | Audit cause |
| ACCOUNT | 25 | Customer account |
| RESP.DEPT | 26 | Primary responsibility (cost center) |
| WASTE.MTL | 27 | Material cost |
| WASTE.ENG | 28 | Engineering hours |
| WASTE.MFG | 29 | Manufacturing hours |
| WASTE.CSS | 30 | Customer service hours |
| ACTIVITY | 35 | Activity log |
| FIRSTACTION.DATE | 38 | First action date |
Lookup Data Sources
The application loads dropdown data from these UniData tables:
| Lookup | Table | Query | Fields |
|---|---|---|---|
| Employees | EMPLOYEE.PUBLIC.1287 | WITH ACTIVE = 'Y' | LAST.NAME, FIRST.NAME, DEPT.NO |
| Accounts | ACCOUNT.1287 | WITH DELETED # 'Y' AND WITH JSHOP.GRP = 'J' | NAME |
| Cost Centers | COCE | SELECT COCE | CCEN (key), DESC |
| Audit Types | Static | - | 01=Internal, 02=External Customer, 03=External Registrar |
| Priorities | Static | - | 1=Critical, 2=High, 3=Medium, 4=Low |
| Departments | /api/redbook/lookups/departments | live from API | code, name, slot (0-based MV index) |
| Projects | /api/redbook/lookups/projects?q= | search-as-you-type, ≥2 chars, max 25 | code, description, customerName |
API Endpoints
RFC Operations
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/redbook/rfc/{rfcNo} | Get single RFC |
| POST | /api/redbook/rfc | Create new RFC |
| PUT | /api/redbook/rfc/{rfcNo} | Update RFC |
| DELETE | /api/redbook/rfc/{rfcNo} | Delete RFC |
| POST | /api/redbook/search | Search RFCs |
| GET | /api/redbook/by-project/{projectNo} | Get RFCs for project |
Lookup Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/redbook/dev/lookups/all | All lookups in one call |
| GET | /api/redbook/dev/lookups/employees | Active employees |
| GET | /api/redbook/dev/lookups/accounts | Job Shop accounts |
| GET | /api/redbook/dev/lookups/cost-centers | Cost centers (COCE) |
| GET | /api/redbook/dev/lookups/audit-types | Audit types |
| GET | /api/redbook/dev/lookups/priorities | Priority levels |
| GET | /api/redbook/dev/lookups/departments | Department codes with slot index |
| GET | /api/redbook/lookups/projects?q= | Project search (≥2 chars, max 25 results) |
| GET | /api/redbook/dev/lookups/projects?q= | Project search (service-account session) |
Project Structure
redbook-web/
├── frontend/
│ ├── src/
│ │ ├── api/
│ │ │ └── client.ts # API client with all endpoints
│ │ ├── auth/
│ │ │ └── AuthProvider.tsx # Azure AD authentication
│ │ ├── components/
│ │ │ ├── rfc/
│ │ │ │ ├── DepartmentStatusGrid.tsx
│ │ │ │ ├── CorrectiveActionForm.tsx
│ │ │ │ ├── ProjectSearch.tsx # async combobox with debounce
│ │ │ │ └── index.ts
│ │ │ └── LoadingSpinner.tsx
│ │ ├── hooks/
│ │ │ ├── useRfc.ts # RFC CRUD hooks
│ │ │ └── useLookups.ts # Lookup data hooks
│ │ ├── pages/
│ │ │ ├── HomePage.tsx # Search/list view
│ │ │ ├── RfcDetailPage.tsx # View RFC details
│ │ │ ├── RfcCreatePage.tsx # Create new RFC
│ │ │ └── RfcEditPage.tsx # Edit existing RFC
│ │ ├── types/
│ │ │ └── redbook.ts # TypeScript interfaces
│ │ └── App.tsx
│ ├── package.json
│ └── vite.config.ts
└── README.md
Development
Prerequisites
- Node.js 18+
- npm or pnpm
- Access to PSI.UniData.API (local or production)
Local Development
cd C:\GIT\redbook-web\frontend
npm install
npm run devThe app runs at http://localhost:5173 and connects to the local API at http://localhost:5000.
Environment Variables
Create .env.local for local development:
VITE_API_URL=http://localhost:5000
VITE_AZURE_CLIENT_ID=your-client-id
VITE_AZURE_TENANT_ID=your-tenant-idDeployment
Azure App Service (Private Endpoint)
The frontend is deployed to Azure App Service with:
- Custom domain: redbook.progressivesurface.com
- Private endpoint:
redbook-web-peat10.160.140.12(PS-ProdData subnet) - Public network access: Disabled (internal only, VPN required)
- SSL: Wildcard certificate (
*.progressivesurface.com) - Auth: Azure AD (EasyAuth)
- CI/CD: GitHub Actions on self-hosted runner (
ps-cicd-runner)
Build and Deploy
Pushes to main or master trigger automatic deployment via GitHub Actions:
npm run build
# Deploys via self-hosted runner → az webapp deployComparison with WPF App
| Feature | WPF App | Web App |
|---|---|---|
| Platform | Windows desktop | Web browser |
| Authentication | Windows domain | Azure AD |
| Data access | PSI Local Service (WCF) | REST API (U2 Toolkit) |
| Performance | Slow (WCF overhead) | Fast (~200-300ms) |
| Deployment | Manual install | Auto-deploy |
| Mobile access | No | Yes |
Parity Harness
A four-layer test harness validates that the REST API (PSI.UniData.API) produces the same redbook data as the legacy WCF backend (PSILocalService), enabling safe incremental migration.
Architecture
| Layer | Technology | Purpose |
|---|---|---|
| Layer 1 — Headless | .NET 8 console (PSI.Redbook.Headless) | Calls both backends, writes normalized Observation JSON |
| Layer 2 — VM Tests | xUnit net48 (PSI.Redbook.View.Tests) | Unit-tests WPF ViewModel behavior (pending Task 11a) |
| Layer 3 — UI Smoke | Manual / future Playwright | End-to-end comparison of rendered UI |
| Layer 4 — XAML Audit | Static analysis | Documents all bound VM properties in the ProApp XAML |
Running Layer 1 Locally
cd C:\GIT\PSI.All\parity\orchestrator
# Run a scenario against both backends
python -m orchestrator run \
--yaml ..\scenarios\get_rfc_by_id.yaml \
--out ..\out \
--wcf-endpoint http://ps-proxy/PSILocalService/PSILocalService.svc \
--rest-base https://api.progressivesurface.com \
--wcf-user <user> \
--wcf-password <password>Scenario Files
Scenario YAMLs live in PSI.All/parity/scenarios/ (moved from redbook-web/harness/scenarios/ in April 2026 to avoid cross-repo CI checkout). Current scenarios:
| Scenario | What it tests |
|---|---|
get_rfc_by_id | Fetch single RFC by number |
get_rfc_list_by_project | RFC list for a project via GetRedbookItems |
search_recent_rfcs | RFC appears in project search results |
CI
PSI.All/.github/workflows/parity.yml runs on the self-hosted, windows, dotnet-framework runner:
- Layer 2 VM tests — every qualifying PR; currently 0 passed / 1 skipped (pending Task 11a)
- Layer 1 parity — nightly Mon–Fri and on manual dispatch; requires
PARITY_*secrets
Related Pages
- PSI.UniData.API - Backend REST API
- Quality Process - Redbook system overview
- Redbook Analysis - Analytics dashboard
- ProApps - Legacy WPF applications
Department Slot Order
Department status writes/reads use 0-based MV slot indices (matching ProApp RFCResponsibleDeptsEnum) as dictionary keys — not dept codes. This matters because two departments share code 180 (HR = slot 13, Accounting = slot 14). The 16-slot order is:
| Slot | Code | Name |
|---|---|---|
| 0 | 150 | Purchasing |
| 1 | 160 | RCV/STK |
| 2 | 170 | Quality |
| 3 | 115 | Proposal |
| 4 | 140 | Sales |
| 5 | 110E | Elec Engineering |
| 6 | 110M | Mech Engineering |
| 7 | 102 | Machine/Weld |
| 8 | 125 | Job Shop |
| 9 | 130 | Process Dev |
| 10 | 106 | Mech Assembly |
| 11 | 108 | Elec Assembly |
| 12 | 120 | Customer Service |
| 13 | 180 | Human Resources |
| 14 | 180 | Accounting |
| 15 | 160Z | Production Planning |
Last updated: 2026-04-29 — dept slot order fix (Plan 3), parity orchestrator moved to PSI.All