From fbe79c07738e21f2021352c95ef37ae900230b0f Mon Sep 17 00:00:00 2001 From: Lorentz Date: Sun, 15 Feb 2026 23:25:24 +0000 Subject: [PATCH] Update CLAUDE.md and TASKS.md to match infrastructure --- TASKS.md | 513 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 TASKS.md diff --git a/TASKS.md b/TASKS.md new file mode 100644 index 0000000..40102eb --- /dev/null +++ b/TASKS.md @@ -0,0 +1,513 @@ +# 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 + +### F-002: Docker & Infrastructure Alignment +- [x] `Dockerfile` for Next.js app (Node.js 22, multi-stage build) +- [x] `docker-compose.yml`: Traefik, postgres:16-alpine, redis:7-alpine, app-dev, app-test, app-prod +- [x] Traefik reverse proxy with Cloudflare DNS challenge for auto-SSL +- [x] `.env.example` with all required variables +- [x] `init-databases.sh` for multi-database PostgreSQL setup +- [x] `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) + +### 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 + +### 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 + +### 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 + +### 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 +- **Deps:** F-001 | **Est:** 3 hrs + +### 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[] +- [ ] Login page at `/login` +- [ ] Forgot password page at `/forgot-password` +- [ ] Account request page at `/account-request` +- [ ] reCAPTCHA integration on public forms +- **Deps:** F-003 | **Est:** 4 hrs + +### F-008: Middleware & Guards +- [ ] `src/middleware.ts` — redirect unauthenticated users to /login +- [ ] Company context middleware: ensure active company is selected +- [ ] `src/lib/permissions.ts` — `requirePermission(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 +- **Deps:** F-007 | **Est:** 4 hrs + +### 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 +- [ ] Breadcrumb component +- **Deps:** F-007 | **Est:** 3 hrs + +### 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) +- [ ] Migrate `alloc_request` + `alloc_request_detail` +- [ ] Migrate supporting tables (notifications, docs, email events, wave, paint) +- [ ] Validation: count comparison, spot-check key records +- [ ] Handle: IDENTITY → SERIAL, datetime2 → TIMESTAMPTZ, bit → BOOLEAN, nvarchar(MAX) → TEXT +- **Deps:** F-005 | **Est:** 6 hrs + +--- + +## 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 + +### A-011: Admin Password Reset Links +- [ ] 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 +- [x] Linux server provisioned (expvtcasp01) +- [x] Docker installed +- [x] Docker Compose installed +- [x] Netbird VPN configured (100.89.62.20) + +### I-002: Forgejo Repository +- [x] Repository created at `forgejo.wulfconsulting.cloud/lorentz/quest-vorteq` +- [x] SSH key configured (port 222) +- [x] Git identity configured on server +- [x] Initial infrastructure files pushed + +### I-003: CI/CD Pipeline +- [x] Forgejo Actions enabled on repository +- [x] Self-hosted runner registered (`expvtcasp01`) using Docker-in-Docker +- [x] Build workflow: checkout → Docker build → push to Forgejo registry +- [x] Deploy workflow: SSH into server → pull → restart containers +- [x] Secrets configured (registry credentials, deploy SSH key) + +### I-004: Infrastructure Stack +- [x] Traefik v3 running with Cloudflare DNS challenge +- [x] SSL certificate issued for `traefik.vorteq.wulf.cloud` +- [x] PostgreSQL 16 running with dev/test/prod databases +- [x] Redis 7 running with separate DB numbers per environment +- [x] Cloudflare DNS A records pointing to Netbird IP + +### I-005: Domain Configuration +- [x] `quest.vorteq.wulf.cloud` → 100.89.62.20 (production) +- [x] `dev.quest.vorteq.wulf.cloud` → 100.89.62.20 (development) +- [x] `testing.vorteq.wulf.cloud` → 100.89.62.20 (testing) +- [x] `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** |