quest-vorteq/TASKS.md
Lorentz a67558de23 feat(F-008,F-009,F-010): complete Phase 1 foundation with middleware, navigation, and migrations
Implements authentication middleware with rate limiting, comprehensive permission
system, full portal navigation with sidebar and header, company selection flow,
and data migration tooling from SQL Server to PostgreSQL.

Key additions:
- Middleware: auth guards, rate limiting, session management
- Permissions: role-based access control with hierarchical permission rules
- Navigation: responsive sidebar with expandable sections, header with notifications
- Company selector: multi-company user support with session-based active company
- Data migration: comprehensive script for migrating auth and quest domain tables
- Schema: added auth_user_type_permission_group junction table

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-16 11:07:44 +00:00

22 KiB

Vorteq Quest Portal - Task Tracking

Reference: docs/Vorteq_Quest_PRD_v1.0.docx for full specifications Instructions: CLAUDE.md for coding conventions and patterns

Mark tasks: [ ] Not started | [~] In progress | [x] Complete | [!] Blocked


Phase 1: Foundation (Est. 30-40 hrs)

F-001: Project Scaffold

  • Initialize Next.js 15 with App Router, TypeScript strict mode
  • Install and configure Tailwind CSS
  • Install and configure shadcn/ui (default theme)
  • Configure ESLint + Prettier
  • Configure tsconfig.json with path aliases (@/)
  • Create base project structure per CLAUDE.md
  • Create .dockerignore for build optimization
  • Deps: None | Est: 2 hrs | Status: Complete

F-002: Docker & Infrastructure Alignment

  • Dockerfile for Next.js app (Node.js 22, multi-stage build)
  • docker-compose.yml: Traefik, postgres:16-alpine, redis:7-alpine, app-dev, app-test, app-prod
  • Traefik reverse proxy with Cloudflare DNS challenge for auto-SSL
  • .env.example with all required variables
  • init-databases.sh for multi-database PostgreSQL setup
  • setup.sh for server provisioning
  • Health check endpoints in Next.js app (/api/health)
  • Verify next.config.js has output: 'standalone' for Docker
  • Deps: F-001 | Est: 1 hr (remaining) | Status: Complete

F-003: Prisma Schema - Auth Domain

  • auth_user table (email, password hash, deactivated, verified, login counts, 2FA, SSO fields)
  • auth_domain table
  • auth_user_type table
  • auth_oauth_client + auth_oauth_access_token + auth_oauth_refresh_token
  • auth_password_history + auth_password_reset
  • auth_permission_group + auth_permission_rule + junction table
  • auth_permission_rule_category
  • auth_security_question + auth_security_answer
  • Run initial migration, verify schema
  • Deps: F-001 | Est: 4 hrs | Status: Complete

F-004: Prisma Schema - Quest Domain

  • quest_company (EpicorCustID, display name, invoicing flags)
  • quest_user (FK to auth_user, IsSubUser, API token)
  • quest_user_company junction table
  • quest_account_request
  • quest_notification + quest_user_notification_alert_read
  • quest_email_event + quest_user_email_event junction
  • quest_email_log
  • quest_plant + quest_inventory_type + quest_inventory_plant
  • quest_processed_order_acknowledgement_email
  • Run migration, verify
  • Deps: F-003 | Est: 3 hrs | Status: Complete

F-005: Prisma Schema - Remaining Domains

  • ship_request + ship_request_detail
  • alloc_request + alloc_request_detail + alloc_plant
  • inv_upload + inv_upload_entry + inv_upload_status
  • finance_ap_check_processing_batch + finance_ap_check_processing_status
  • paint_plant + paint_line + paint_line_type + paint_plant_line + paint_schedule
  • doc_article + doc_category + doc_article_permission_group
  • wave_process + wave_process_history
  • lts_task + lts_task_type + lts_task_schedule + related tables
  • Run migration, verify all relationships
  • Deps: F-004 | Est: 4 hrs | Status: Complete

F-006: Epicor MSSQL Connection Service

  • src/lib/epicor.ts — connection pool with mssql package
  • execStoredProc() helper with typed params
  • execQuery() helper for raw SQL queries
  • Connection health check function
  • Graceful error handling (connection timeout, query failure)
  • Type definitions for Epicor query results in src/types/epicor.ts
  • [~] Test connection against Epicor10Live with portal user (pending actual credentials)
  • Deps: F-001 | Est: 3 hrs | Status: Complete

F-007: Better Auth Setup

  • Install and configure Better Auth
  • Credentials provider: validate against auth_user table (bcrypt)
  • Session strategy with user ID, email, role, company context
  • [~] Session includes: userId, questUserId, activeCompanyId, isSubUser, permissionRules[] (requires middleware enhancement)
  • Login page at /login
  • Forgot password page at /forgot-password
  • Account request page at /account-request
  • reCAPTCHA integration on public forms (deferred to later)
  • Deps: F-003 | Est: 4 hrs | Status: Complete

F-008: Middleware & Guards

  • src/middleware.ts — redirect unauthenticated users to /login
  • Company context middleware: ensure active company is selected
  • src/lib/permissions.tsrequirePermission(rule) helper
  • Admin route protection (check user type)
  • Sub-user detection helper (isSubUser())
  • Company selector: /select-company page for multi-company users
  • Rate limiting on auth endpoints
  • Session management utilities for Quest-specific data
  • API route for setting active company
  • Deps: F-007 | Est: 4 hrs | Status: Complete

F-009: Base Layout & Navigation

  • Root layout with sidebar navigation
  • Header: company name, user menu, notification bell
  • Sidebar: Dashboard, Inventory, Orders, Shipments, Coil Activity, Invoices, Requests
  • Admin section in sidebar (conditional on role)
  • Company switcher dropdown (admin) or display (customer)
  • [~] Responsive: collapsible sidebar on mobile (basic implementation, needs enhancement)
  • Breadcrumb component
  • Deps: F-007 | Est: 3 hrs | Status: Complete

F-010: Data Migration Scripts

  • scripts/migrate-data.ts — connects to both SQL Server and PostgreSQL
  • Migrate auth_user + auth_user_type + auth_domain (preserve bcrypt hashes)
  • Migrate auth_permission_group + auth_permission_rule + junction
  • Migrate quest_user + quest_company + quest_user_company
  • Migrate quest_plant + quest_inventory_type/plant
  • Migrate quest_email_event
  • Migrate ship_request + ship_request_detail (historical data) (deferred - can be done before go-live)
  • Migrate alloc_request + alloc_request_detail (deferred - can be done before go-live)
  • Migrate supporting tables (notifications, docs, email events, wave, paint) (deferred - can be done before go-live)
  • Validation: count comparison, spot-check key records
  • Handle: IDENTITY → SERIAL, datetime2 → TIMESTAMPTZ, bit → BOOLEAN, nvarchar(MAX) → TEXT
  • Deps: F-005 | Est: 6 hrs | Status: Complete (core tables migrated)

Phase 2: Core Features (Est. 80-110 hrs)

C-001: Dashboard

  • Dashboard page at /(portal)/dashboard/page.tsx
  • Server component: fetch top 5 orders, top 5 shipments from Epicor
  • Summary cards: inventory overview, recent activity
  • Notification alert banner (unread alerts)
  • Quick-nav cards to major features
  • Loading skeletons for async data
  • Deps: F-009, F-006 | Est: 6 hrs

C-002: Inventory Summary Views

  • /(portal)/inventory/page.tsx — category selector
  • /(portal)/inventory/[category]/page.tsx — summary data table
  • Service: src/services/inventory.ts
    • getWorkInProgressSummary(custId, dbName, sub)
    • getFinishedGoodsSummary(custId, dbName, sub)
    • getProcessedOtherSummary(custId, dbName, sub)
    • getUnprocessedSummary(custId, dbName) — block sub-users
    • getUnprocessedRRSummary(custId, dbName) — block sub-users
    • getProcessedRRSummary(custId, dbName) — block sub-users
  • V6 procedures: pass sub param (0/1 based on user)
  • Row count pre-fetch for detail drill-down (ALLV6 with count flag)
  • Data table with sorting, filtering, export to CSV
  • Click row → navigate to detail view
  • Deps: F-006, F-009 | Est: 12 hrs

C-003: Inventory Detail Views

  • /(portal)/inventory/[category]/details/page.tsx
  • Query params: part, plant, warehouse (for specific) or none (for all)
  • Service functions for each detail stored procedure (V6 and legacy)
  • Paint code display (from Epicor Part_UD table)
  • Part description with paint code lookup (getVorPartDescriptionFromPartNumberWithPaintCode)
  • On-hand quantity for specific lines
  • Data table with full column set
  • Back navigation to summary
  • Deps: C-002 | Est: 8 hrs

C-004: Order List

  • /(portal)/orders/page.tsx
  • Service: getTop100Orders(custId) using portal_Orders.sql
  • HDC/HDM exception handling (use portal_OrdersHDC view, map CustID)
  • Data table: Order Number, PO, Customer Part, Vorteq Part, Dates, Qty, Status
  • Click-through to order acknowledgement
  • Deps: F-006, F-009 | Est: 6 hrs

C-005: Order Acknowledgement Detail + PDF

  • /(portal)/orders/[id]/page.tsx
  • Service: getAcknowledgement(customer, poNumber?, orderNum?) using SalesOrder.sql
  • Detail view with all order line information
  • PDF export button (generate PDF server-side)
  • Deps: C-004 | Est: 6 hrs

C-006: Shipment List

  • /(portal)/shipments/page.tsx
  • Service: getTop100Shipments(custId, dbName) via portal_GetShipmentsV1
  • ShipToLoc formatting (replace , with line breaks)
  • Data table with shipment details
  • Click-through to BOL detail
  • Deps: F-006, F-009 | Est: 5 hrs

C-007: BOL Detail + PDF

  • /(portal)/shipments/[bol]/page.tsx
  • Service: getBOL(bolNum, custId) using getBOL.sql
  • Detail view with line items
  • PDF export
  • Deps: C-006 | Est: 4 hrs

C-008: Coil Activity - Usage Report

  • /(portal)/coil-activity/usage/page.tsx
  • Date range picker (with validation)
  • Service: getCoilActivityUsage(custId, startDate, endDate) using portal_CoilActivityUsage.sql
  • Deduplication logic: only show OnHandQty for most recent DateUsed per LotNum
  • Zero weights → null display
  • Data table with full column set
  • Deps: F-006, F-009 | Est: 6 hrs

C-009: Coil Activity - Receipts Report

  • /(portal)/coil-activity/receipts/page.tsx
  • Date range picker
  • Service: getCoilActivityReceipts(custId, startDate, endDate)
  • VGL customer exception (use special SQL)
  • Data table with receipt columns
  • Deps: F-006, F-009 | Est: 4 hrs

C-010: Coil-by-Coil Report

  • /(portal)/coil-activity/coil-by-coil/page.tsx
  • Job number search input
  • Service: getCoilByCoil(jobNum, customer) using CoilByCoil.sql
  • Detail display
  • Deps: F-006 | Est: 3 hrs

C-011: Job Status by Plant

  • /(portal)/jobs/status/page.tsx
  • Service: getJobStatusByPlantByCustomer(custId, dbName) using JobStatusByPlantByCustomer.sql
  • Note: requires DBNAME cross-database parameter
  • Data table grouped by plant
  • Deps: F-006 | Est: 3 hrs

C-012: Job Traveler + PDF

  • /(portal)/jobs/[jobNum]/traveler/page.tsx
  • Service: getJobTraveler(jobNum) using jobTraveler.sql
  • Detailed routing/operations view
  • PDF export
  • Deps: F-006 | Est: 4 hrs

C-013: Shipment Request Cart Workflow

  • /(portal)/shipment-requests/page.tsx — list existing requests
  • /(portal)/shipment-requests/new/page.tsx — new request flow
  • /(portal)/shipment-requests/[id]/page.tsx — edit/view request
  • Step 1: Select ship-to address (from portal_CustomerShipToAddresses)
    • Sub-user filter: only '%NB HANDY%' addresses
  • Step 2: Add inventory items to cart
    • Browse available inventory, select items
    • Set quantity per line item
  • Step 3: Enter details (Order#, PO#, Release#, Pickup Date, Instructions, Email Recipients)
  • Submit: create ship_request + ship_request_detail records, send emails
  • Cancel: set IsCancelled=true, record canceller, send cancellation emails
  • Cart persistence: save progress to DB, show LastCartActivity
  • Confirmation page after submit
  • Deps: F-005, C-003 | Est: 12 hrs

C-014: Coil Allocation Request Cart Workflow

  • Mirror of C-013 but for allocation requests
  • Additional field: JobNum (from Epicor)
  • Uses alloc_request + alloc_request_detail tables
  • getCoilAllocationsByJobNumber() for allocation data
  • Deps: F-005, C-003 | Est: 8 hrs

C-015: Invoice Viewing (Customer)

  • /(portal)/invoices/page.tsx
  • Only visible if company CanAccessInvoices=true
  • List invoices from inv_upload_entry filtered by company
  • PDF download endpoint /api/invoices/:id/pdf
  • File serving from storage directory
  • Deps: F-005, F-009 | Est: 4 hrs

C-016: Notifications Display

  • Notification bell in header with unread count
  • Alert banner on dashboard for IsAlert=true notifications
  • Notification list view
  • Mark as read → create quest_user_notification_alert_read record
  • Deps: F-005, F-009 | Est: 3 hrs

Phase 3: Admin Features (Est. 60-80 hrs)

A-001: User Management

  • /(portal)/admin/users/page.tsx — user list with search/filter
  • /(portal)/admin/users/[id]/page.tsx — user detail/edit
  • /(portal)/admin/users/new/page.tsx — create user
  • CRUD operations via server actions
  • Assign/remove company associations
  • Assign permission groups
  • Activate/deactivate users
  • Set IsSubUser flag
  • View login history (success/failure counts)
  • Deps: F-007, F-005 | Est: 8 hrs

A-002: Company Management

  • /(portal)/admin/companies/page.tsx — company list
  • /(portal)/admin/companies/[id]/page.tsx — company detail/edit
  • Edit: display name, InvoicingEmail, OrderAckEmail, CanAccessInvoices, ReceivesSOEmails
  • Activate/deactivate companies
  • View associated users
  • Deps: F-005 | Est: 5 hrs

A-003: Account Request Management

  • /(portal)/admin/account-requests/page.tsx
  • List pending requests
  • Approve: create auth_user + quest_user + company association
  • Reject: mark RequestProcessed without creating user
  • Send approval/rejection email
  • Deps: F-005, A-001 | Est: 4 hrs

A-004: Invoice Upload + Processing

  • /(portal)/admin/invoices/upload/page.tsx
  • Multi-page PDF upload
  • Parse PDF to extract individual invoices by invoice number
  • Match to CustID via getCustomerIDsForInvoiceNumbers()
  • Match to JobNum via getJobNumberByInvoiceNumber()
  • Create inv_upload + inv_upload_entry records
  • Status tracking (upload status, per-entry errors)
  • Send invoices to companies with InvoicingEmail
  • Handle: ignored entries, replaced entries, send errors
  • Upload history view
  • Deps: F-005, C-015 | Est: 12 hrs

A-005: AP Check Processing

  • /(portal)/admin/ap-check/page.tsx
  • Excel upload (XLS/XLSX)
  • Extract Group ID from cell AB9
  • Query Epicor CheckLines.sql by Group ID
  • Generate CSV with specific formatting
    • Country code handling: 'USA' vs 'FOR' (foreign)
    • Address field processing
  • Download generated CSV
  • Batch status tracking in finance_ap_check_processing_batch
  • Deps: F-005 | Est: 8 hrs

A-006: Notification Management

  • /(portal)/admin/notifications/page.tsx — list + CRUD
  • Create: Title, Text, IsAlert, Date, Time
  • Edit/delete existing notifications
  • Preview notification as customer would see it
  • Deps: F-005 | Est: 3 hrs

A-007: Documentation / Knowledge Base

  • /(portal)/admin/documentation/page.tsx — category + article management
  • Category CRUD (name, icon)
  • Article CRUD (content, category assignment)
  • Permission group assignment per article
  • Customer-facing documentation viewer
  • Deps: F-005 | Est: 5 hrs

A-008: Shipping Reports

  • /(portal)/admin/shipping-reports/page.tsx
  • View generated reports (stored Excel files)
  • Manual trigger: generate previous month report
  • Manual trigger: generate YTD report
  • Service: getShippingForMonth(), getShippingYTD() via Epicor
  • Excel generation using a library (e.g., ExcelJS)
  • Deps: F-006 | Est: 4 hrs

A-009: Wave/EDI Process Management

  • /(portal)/admin/wave/page.tsx
  • List wave_process entries with enable/disable toggles
  • View wave_process_history with run results
  • Manual trigger for processes
  • Minimal implementation — client migrating EDI to another product
  • Deps: F-005 | Est: 4 hrs

A-010: Paint Schedule Management

  • /(portal)/admin/paint-schedule/page.tsx
  • CRUD for paint schedule entries
  • Plant/line selection from paint_plant, paint_line, paint_plant_line
  • Job lookup from Epicor (getJobForPaintLine, getJob)
  • Schedule view (calendar or table format)
  • Fields: JobID, PartDescription, Customer, Qty, EstimatedRunTime, StartTime, Notes, Complete, LineSpeed
  • Deps: F-005, F-006 | Est: 6 hrs
  • Generate password reset link for any user
  • Create auth_password_reset record with token
  • Optional: bypass minimum time restriction (min_time_bypass)
  • Copy link to clipboard for admin to send manually
  • Deps: F-007 | Est: 2 hrs

Phase 4: Background Jobs & Polish (Est. 30-40 hrs)

J-001: BullMQ Infrastructure

  • Install and configure BullMQ with Redis connection
  • src/jobs/worker.ts — worker process setup
  • Job registration and scheduling system
  • Dashboard: Bull Board or similar for job monitoring (admin only)
  • Retry logic with exponential backoff
  • Dead letter queue for failed jobs
  • Deps: F-002 | Est: 4 hrs

J-002: Shipment Request Nag Job

  • Runs every 15 minutes
  • Find open (unsubmitted) shipment requests
  • Send reminder email to request owner
  • Track LastNagDT to avoid duplicate nags
  • Respects withoutOverlapping (skip if previous run still active)
  • Deps: J-001, C-013 | Est: 3 hrs

J-003: Order Acknowledgement Email Job

  • Runs hourly
  • Query Epicor for new orders (getOrdersForCustomerOnOrAfterDate)
  • Cross-reference against quest_processed_order_acknowledgement_email
  • Send to companies with ReceivesSOEmails=true and OrderAckEmail set
  • Record in quest_processed_order_acknowledgement_email to prevent duplicates
  • Deps: J-001, C-004 | Est: 4 hrs

J-004: Invoice Cleanup Job

  • Runs daily
  • disc:clearoldinvoices equivalent
  • Clean up old/expired invoice temporary data
  • Deps: J-001, A-004 | Est: 2 hrs

J-005: Shipping Report Generation Jobs

  • Monthly report: 1st of month at 3am
    • Call getShippingForMonth(lastMonth)
    • Generate Excel file, save to storage
  • YTD report: twice daily
    • Call getShippingYTD()
    • Generate/overwrite Excel file
  • Deps: J-001, A-008 | Est: 4 hrs

J-006: Wave EDI End-of-Month Job

  • Runs monthly on 1st
  • Execute Wave EDI end-of-month process
  • Log results to wave_process_history
  • Send completion email
  • Minimal implementation per client direction
  • Deps: J-001, A-009 | Est: 3 hrs

J-007: Email Service (Microsoft Graph)

  • src/lib/email.ts — Microsoft Graph API client
  • OAuth2 client credentials flow (tenant + client ID + secret)
  • Send email function: sendEmail(to, subject, htmlBody, from?)
  • Email logging to quest_email_log
  • Template system for common emails (request confirmation, nag, acknowledgement)
  • Error handling + retry for transient Graph API failures
  • Deps: F-001 | Est: 5 hrs

J-008: PDF Generation Service

  • src/lib/pdf.ts — PDF generation abstraction
  • Method 1: Puppeteer (render HTML → PDF, replaces wkhtmltopdf)
  • Method 2: @react-pdf/renderer for structured documents
  • Templates: Order Acknowledgement, BOL, Job Traveler, Invoice
  • Consistent header/footer styling
  • Deps: F-001 | Est: 4 hrs

J-009: E2E Test Suite

  • Playwright configuration
  • Test: Login flow (success + failure + rate limiting)
  • Test: Dashboard loads with data
  • Test: Inventory browse → category → summary → detail drill-down
  • Test: Shipment request full workflow (create → add items → submit)
  • Test: Admin invoice upload
  • Test: Company switcher (admin)
  • Data validation tests comparing Epicor query results
  • Deps: All C-* tasks | Est: 8 hrs

J-010: Production Deployment & Cutover

  • Verify all Traefik routes serving correctly with SSL
  • Run data migration scripts against production database
  • Verify all Epicor connections from expvtcasp01
  • DNS cutover: update quest.vorteq.wulf.cloud or portal.vorteqcoil.com as needed
  • Smoke test all features post-cutover
  • Rollback plan documented
  • Security hardening: change Traefik dashboard password, secure .env, firewall rules
  • Deps: All tasks | Est: 4 hrs

Infrastructure Tasks (Completed)

I-001: Server Provisioning

  • Linux server provisioned (expvtcasp01)
  • Docker installed
  • Docker Compose installed
  • Netbird VPN configured (100.89.62.20)

I-002: Forgejo Repository

  • Repository created at forgejo.wulfconsulting.cloud/lorentz/quest-vorteq
  • SSH key configured (port 222)
  • Git identity configured on server
  • Initial infrastructure files pushed

I-003: CI/CD Pipeline

  • Forgejo Actions enabled on repository
  • Self-hosted runner registered (expvtcasp01) using Docker-in-Docker
  • Build workflow: checkout → Docker build → push to Forgejo registry
  • Deploy workflow: SSH into server → pull → restart containers
  • Secrets configured (registry credentials, deploy SSH key)

I-004: Infrastructure Stack

  • Traefik v3 running with Cloudflare DNS challenge
  • SSL certificate issued for traefik.vorteq.wulf.cloud
  • PostgreSQL 16 running with dev/test/prod databases
  • Redis 7 running with separate DB numbers per environment
  • Cloudflare DNS A records pointing to Netbird IP

I-005: Domain Configuration

  • quest.vorteq.wulf.cloud → 100.89.62.20 (production)
  • dev.quest.vorteq.wulf.cloud → 100.89.62.20 (development)
  • testing.vorteq.wulf.cloud → 100.89.62.20 (testing)
  • traefik.vorteq.wulf.cloud → 100.89.62.20 (dashboard)

Summary

Phase Tasks Est. Hours (Low) Est. Hours (High)
Phase 1: Foundation F-001 to F-010 28 38
Phase 2: Core Features C-001 to C-016 80 110
Phase 3: Admin Features A-001 to A-011 60 80
Phase 4: Jobs & Polish J-001 to J-010 30 40
Infrastructure I-001 to I-005 Done Done
TOTAL 52 tasks 198 268