Redbook Web
Modern web application for managing RFC (Request for Change) quality issue tickets, replacing the legacy WPF Redbook application.
Overview
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