quest-vorteq/CLAUDE.md
Lorentz 6ef918aad4
Some checks failed
Build and Deploy / build (push) Failing after 5s
Build and Deploy / deploy (push) Has been skipped
Add reference docs and update CLAUDE.md
2026-02-15 23:33:57 +00:00

13 KiB

Vorteq Quest Portal - Claude Code Instructions

Project Overview

Rebuilding the Vorteq Quest customer portal from a legacy Laravel 9 / Windows Server / Savvior stack to a modern Next.js / TypeScript / PostgreSQL stack running in Docker on Linux. The full PRD is in docs/Vorteq_Quest_PRD_v1.0.docx.

Tech Stack

  • Runtime: Node.js 22 LTS
  • Framework: Next.js 15 with App Router
  • Language: TypeScript (strict mode)
  • ORM (App DB): Prisma with PostgreSQL 16
  • ORM (Epicor): mssql package (read-only connection to SQL Server)
  • Auth: Better Auth with credentials provider
  • UI: shadcn/ui + Tailwind CSS
  • Data Tables: TanStack Table v8
  • State: React Server Components by default, client components only when needed
  • Cache/Queue: Redis 7 via BullMQ
  • Email: Microsoft Graph API (Office 365)
  • PDF: Puppeteer or @react-pdf/renderer
  • Testing: Vitest (unit/integration), Playwright (E2E)
  • Containers: Docker + Docker Compose
  • Reverse Proxy: Traefik v3 with Cloudflare DNS challenge for SSL
  • CI/CD: Forgejo Actions with self-hosted runner
  • Registry: Forgejo built-in OCI container registry

Reference Documentation\


All legacy system documentation is in the docs/ directory:
\

  • vorteqcoil-portal-documentation.md — Complete interface documentation of the legacy portal: navigation, sections, column layouts, business logic. Reference when building UI pages.\
  • vorteqcoil-visual-documentation.html — Visual reference guide with color coding system and UI patterns. Reference for design decisions.\
  • VorteqDiscovery_20260206_205453.txt — Server infrastructure discovery: IIS config, scheduled tasks, services.\
  • VorteqAppDiscovery_20260206_210519.txt — Application code discovery: Laravel routes, controllers, models, views, Blade templates. Reference when implementing features.\
  • VorteqEpicorDB_20260210_211850.txt — Epicor database schema: tables, stored procedures, views. Reference when writing Epicor queries.\
  • EpicorDatabase.php — Legacy PHP database class with all Epicor query implementations. Primary reference for porting queries to TypeScript.\
  • _FILE_LISTING.txt — Complete file listing of the legacy Laravel application.\
  • _MODEL_MAP.txt — Legacy Eloquent model-to-table mappings. Reference for Prisma schema design.\
  • _SQL_QUERIES_FOUND.txt — All SQL queries extracted from the legacy codebase. Reference for service layer implementation.

    ' CLAUDE.md

Infrastructure

Server

  • Host: expvtcasp01 (Linux/Ubuntu)
  • Netbird IP: 100.89.62.20
  • Stack directory: /opt/stacks/vorteq
  • Runner directory: /opt/stacks/forgejo-runner

Repository

  • URL: forgejo.wulfconsulting.cloud/lorentz/quest-vorteq
  • SSH: ssh://git@forgejo.wulfconsulting.cloud:222/lorentz/quest-vorteq.git (port 222)
  • Registry: forgejo.wulfconsulting.cloud/lorentz/quest-vorteq

Domains

  • Production: quest.vorteq.wulf.cloud
  • Development: dev.quest.vorteq.wulf.cloud
  • Testing: testing.vorteq.wulf.cloud
  • Traefik Dashboard: traefik.vorteq.wulf.cloud

Running Services

  • Traefik: Reverse proxy, auto-SSL via Cloudflare DNS challenge
  • PostgreSQL 16: Three databases — vorteq_dev, vorteq_test, vorteq_prod
  • Redis 7: Three DB numbers — 0 (dev), 1 (test), 2 (prod)
  • Forgejo Runner: Docker-in-Docker, registered as expvtcasp01

CI/CD Pipeline

Push to main → build Docker image → tag as :latest → push to Forgejo registry → deploy to prod Push to development → build Docker image → tag as :dev → push to Forgejo registry → deploy to dev

Project Structure

quest-vorteq/
├── CLAUDE.md                    # This file
├── TASKS.md                     # Task tracking
├── docker-compose.yml           # Infrastructure: traefik, postgres, redis, app containers
├── Dockerfile                   # Multi-stage Next.js build
├── .forgejo/
│   └── workflows/
│       └── deploy.yml           # CI/CD pipeline
├── traefik/
│   ├── traefik.yml              # Traefik configuration
│   ├── acme.json                # SSL certificates (gitignored)
│   └── config/                  # Dynamic Traefik config
├── prisma/
│   ├── schema.prisma            # PostgreSQL schema
│   ├── migrations/
│   └── seed.ts                  # Seed data / migration scripts
├── src/
│   ├── app/                     # Next.js App Router
│   │   ├── (auth)/              # Auth route group (login, forgot-password, account-request)
│   │   ├── (portal)/            # Authenticated route group
│   │   │   ├── dashboard/
│   │   │   ├── inventory/
│   │   │   ├── orders/
│   │   │   ├── shipments/
│   │   │   ├── coil-activity/
│   │   │   ├── invoices/
│   │   │   ├── shipment-requests/
│   │   │   ├── allocation-requests/
│   │   │   ├── jobs/
│   │   │   └── admin/           # Admin-only pages
│   │   ├── api/                 # API routes
│   │   └── layout.tsx
│   ├── components/
│   │   ├── ui/                  # shadcn/ui components
│   │   ├── data-table/          # Reusable TanStack Table wrapper
│   │   ├── layout/              # Navigation, header, sidebar
│   │   └── forms/               # Shared form components
│   ├── lib/
│   │   ├── db.ts                # Prisma client singleton
│   │   ├── epicor.ts            # MSSQL connection + query helpers
│   │   ├── auth.ts              # Better Auth configuration
│   │   ├── email.ts             # Microsoft Graph email service
│   │   ├── pdf.ts               # PDF generation service
│   │   ├── permissions.ts       # RBAC helpers
│   │   └── utils.ts             # General utilities
│   ├── services/
│   │   ├── inventory.ts         # Inventory data access (calls Epicor SPs)
│   │   ├── orders.ts            # Order data access
│   │   ├── shipments.ts         # Shipment data access
│   │   ├── coil-activity.ts     # Coil activity data access
│   │   ├── ship-requests.ts     # Shipment request business logic
│   │   ├── alloc-requests.ts    # Allocation request business logic
│   │   └── invoices.ts          # Invoice processing
│   ├── jobs/
│   │   ├── worker.ts            # BullMQ worker setup
│   │   ├── shipment-nag.ts
│   │   ├── order-ack-email.ts
│   │   ├── invoice-cleanup.ts
│   │   ├── shipping-reports.ts
│   │   └── wave-edi.ts
│   └── types/
│       ├── epicor.ts            # Epicor query result types
│       ├── portal.ts            # Portal-specific types
│       └── index.ts
├── tests/
│   ├── unit/
│   ├── integration/
│   └── e2e/
├── scripts/
│   └── migrate-data.ts          # SQL Server → PostgreSQL migration
└── docs/
    └── Vorteq_Quest_PRD_v1.0.docx

Coding Conventions

General

  • TypeScript strict mode, no any types
  • Use type over interface unless extending
  • Prefer const over let, never var
  • Use named exports, not default exports (except page.tsx and layout.tsx)
  • Error handling: use typed error classes, not string throws
  • All API responses: { data: T } on success, { error: string, code: string } on failure

Naming

  • Files: kebab-case (shipment-requests.ts)
  • Components: PascalCase (ShipmentRequestForm.tsx)
  • Functions/variables: camelCase
  • Database columns (Prisma): snake_case
  • API routes: kebab-case (/api/shipment-requests)
  • Environment variables: SCREAMING_SNAKE_CASE

Database

  • Prisma schema uses snake_case for all fields
  • Map legacy PascalCase column names with @map("LegacyName") where needed for migration clarity
  • Always use parameterized queries for Epicor MSSQL — never string interpolation
  • Epicor queries go in src/services/ with typed return values
  • PostgreSQL queries go through Prisma client exclusively

Epicor Connection Pattern

// src/lib/epicor.ts
import sql from 'mssql';

const epicorPool = new sql.ConnectionPool({
  server: process.env.MSSQL_HOST!,
  database: process.env.MSSQL_DATABASE!,
  user: process.env.MSSQL_USER!,
  password: process.env.MSSQL_PASSWORD!,
  options: { encrypt: false, trustServerCertificate: true },
});

// Always use parameterized queries
export async function execStoredProc(name: string, params: Record<string, unknown>) {
  const pool = await epicorPool.connect();
  const request = pool.request();
  for (const [key, value] of Object.entries(params)) {
    request.input(key, value);
  }
  return request.execute(name);
}

Inventory Query Pattern

The Epicor stored procedures require three standard parameters:

  • CustID: The selected company's EpicorCustID
  • DBNAME: The VorteqPortal database name wrapped in brackets, e.g., [VorteqPortal]
  • sub: 0 or 1, indicating if the current user is a sub-user (IsSubUser flag)

V6 stored procedures (WIP, Finished Goods, Processed Other) use the sub parameter. Older procedures (Unprocessed, R&R variants) block sub-users entirely — return empty array if isSubUser.

Component Pattern

// Server component by default
export async function InventorySummary({ category }: { category: string }) {
  const data = await getInventorySummary(category);
  return <DataTable columns={columns} data={data} />;
}

// Client component only when interactivity needed
'use client';
export function ShipmentRequestCart() { ... }

Auth Pattern (Better Auth)

// src/lib/auth.ts
import { betterAuth } from 'better-auth';

export const auth = betterAuth({
  secret: process.env.BETTER_AUTH_SECRET,
  baseURL: process.env.BETTER_AUTH_URL,
  database: {
    type: 'postgres',
    url: process.env.DATABASE_URL,
  },
  // Credentials provider: validate against auth_user table (bcrypt)
  // Session includes: userId, questUserId, activeCompanyId, isSubUser, permissionRules[]
});

Middleware Pattern

// src/middleware.ts - protect all (portal) routes
// src/lib/permissions.ts - check specific permission rules
export function requirePermission(rule: string) {
  // Check user's permission groups contain the rule
}

Important Business Logic

Company Context

After login, the user's active company is stored in the session. All Epicor queries are scoped to that company's EpicorCustID. Admin users can switch companies.

Sub-User Restrictions

Users with IsSubUser=true on their quest_User record:

  • See filtered inventory data (sub=1 parameter to V6 stored procedures)
  • See filtered ship-to addresses (only those matching '%NB HANDY%')
  • Cannot access Unprocessed inventory or R&R data at all

HDC/HDM Customer Exception

Customer 'HDC' uses a special order view (portal_OrdersHDC) and maps to CustID 'HDM' in queries. This needs to be handled in the order service.

VGL Customer Exception

Customer 'VGL' uses a special SQL query for coil receipts instead of the standard view.

Environment Variables

# Application
NODE_ENV=development|test|production
BETTER_AUTH_URL=https://dev.quest.vorteq.wulf.cloud
BETTER_AUTH_SECRET=<generated-by-setup-script>

# PostgreSQL (per-environment, set in docker-compose.yml)
DATABASE_URL=postgresql://postgres:<password>@postgres:5432/vorteq_dev

# Redis (per-environment, set in docker-compose.yml)
REDIS_URL=redis://redis:6379/0

# Epicor SQL Server (read-only)
MSSQL_HOST=wi-e10test
MSSQL_DATABASE=Epicor10Live
MSSQL_USER=portal
MSSQL_PASSWORD=<from-current-env>
MSSQL_PORT=1433

# Portal database name (for cross-DB stored procedure parameter)
PORTAL_DB_NAME=VorteqPortal

# Microsoft Graph (Email)
MS_GRAPH_CLIENT_ID=629eca61-d554-4763-b14a-565aee7b7ab8
MS_GRAPH_TENANT_ID=291e541e-003c-463c-8834-863b9e7c1ebe
MS_GRAPH_CLIENT_SECRET=<from-current-env>
MAIL_FROM=quest@vorteqcoil.com

# reCAPTCHA
RECAPTCHA_SITE_KEY=<from-current-env>
RECAPTCHA_SECRET_KEY=<from-current-env>

# Cloudflare (Traefik SSL - set in .env at stack level)
CLOUDFLARE_DNS_API_TOKEN=<from-cloudflare>

Testing Requirements

  • Unit tests for all service functions (inventory, orders, shipments data transformations)
  • Integration tests for API routes with mocked DB connections
  • E2E tests for: login flow, inventory browse + drill-down, shipment request workflow, invoice upload
  • Data validation: compare Epicor query results with known-good outputs from the legacy system

Git Workflow

  • main branch: production-ready code → deploys to quest.vorteq.wulf.cloud
  • development branch: integration branch → deploys to dev.quest.vorteq.wulf.cloud
  • Feature branches: feature/F-001-project-scaffold
  • Commit messages: feat(F-001): scaffold Next.js project with TypeScript + Tailwind
  • Run npm run lint && npm run typecheck && npm test before committing
  • Push triggers Forgejo Actions → Docker build → registry push → deploy

Task Tracking

All tasks are tracked in TASKS.md. Mark tasks as:

  • [ ] Not started
  • [~] In progress
  • [x] Complete
  • [!] Blocked (note blocker in comments)