Skip to content

AI Prompt: Forma3D.Connect — Phase 0: Foundation ✅

Purpose: This prompt instructs an AI to generate the complete Phase 0 foundation for Forma3D.Connect
Estimated Effort: 20 hours
Output: Working Nx monorepo with all infrastructure in place
Status:COMPLETED — January 2026


🎯 Mission

You are building the foundation for Forma3D.Connect, an integration platform that connects a 3D print farm's e-commerce (Shopify) with print farm management (SimplyPrint). Your task is to execute Phase 0: Foundation — setting up the complete project infrastructure.


📋 Project Context

What is Forma3D.Connect?

Forma3D.Connect automates the print-on-demand workflow: 1. Shopify orders trigger print jobs in SimplyPrint 2. Completed prints trigger fulfillment in Shopify 3. Shipping labels are generated via Sendcloud

Vision Statement

"To create a seamless, automated bridge between e-commerce orders and 3D print production, enabling true print-on-demand operations where every Shopify order automatically triggers production, and every completed print automatically fulfills customer orders."


🛠️ Tech Stack (MANDATORY)

You MUST use exactly these technologies. Do not substitute or add others without explicit approval.

Frontend

Technology Version Purpose
React 19.x UI framework
TypeScript 5.x Language
Vite Latest Bundler
Tailwind CSS 3.x Styling
TanStack Query 5.x Server state
React Router 6.x Routing

Backend

Technology Version Purpose
Node.js 20.x LTS Runtime
NestJS 10.x Framework
TypeScript 5.x Language
Socket.IO 4.x Real-time

Database

Technology Version Purpose
PostgreSQL 16.x Database
Prisma 5.x ORM

Infrastructure

Technology Purpose
Nx 19.x
pnpm 9.x
Vitest Frontend testing
Jest Backend testing
Azure DevOps Pipelines CI/CD

📁 Required Project Structure

Create this EXACT folder structure:

forma-3d-connect/
├── apps/
│   ├── api/                    # NestJS backend
│   │   ├── src/
│   │   │   ├── main.ts
│   │   │   ├── app.module.ts
│   │   │   ├── app.controller.ts
│   │   │   ├── app.service.ts
│   │   │   ├── config/
│   │   │   │   ├── config.module.ts
│   │   │   │   ├── configuration.ts
│   │   │   │   └── env.validation.ts
│   │   │   ├── health/
│   │   │   │   ├── health.module.ts
│   │   │   │   └── health.controller.ts
│   │   │   └── database/
│   │   │       ├── database.module.ts
│   │   │       └── prisma.service.ts
│   │   ├── test/
│   │   │   └── app.e2e-spec.ts
│   │   ├── project.json
│   │   ├── tsconfig.app.json
│   │   ├── tsconfig.spec.json
│   │   └── jest.config.ts
│   │
│   └── web/                    # React 19 frontend
│       ├── src/
│       │   ├── main.tsx
│       │   ├── app.tsx
│       │   ├── router.tsx
│       │   ├── index.css
│       │   ├── components/
│       │   │   └── layout/
│       │   │       ├── root-layout.tsx
│       │   │       ├── sidebar.tsx
│       │   │       └── header.tsx
│       │   ├── pages/
│       │   │   ├── dashboard.tsx
│       │   │   └── not-found.tsx
│       │   ├── hooks/
│       │   │   └── use-health.ts
│       │   └── lib/
│       │       └── api-client.ts
│       ├── index.html
│       ├── public/
│       ├── project.json
│       ├── tsconfig.app.json
│       ├── tsconfig.spec.json
│       ├── vite.config.ts
│       ├── tailwind.config.js
│       └── postcss.config.js
│
├── libs/
│   ├── domain/                 # Shared types and business logic
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── entities/
│   │   │   │   ├── order.ts
│   │   │   │   ├── line-item.ts
│   │   │   │   ├── print-job.ts
│   │   │   │   ├── product-mapping.ts
│   │   │   │   ├── assembly-part.ts
│   │   │   │   ├── printer.ts
│   │   │   │   └── event-log.ts
│   │   │   ├── enums/
│   │   │   │   ├── order-status.ts
│   │   │   │   ├── line-item-status.ts
│   │   │   │   ├── print-job-status.ts
│   │   │   │   └── event-severity.ts
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── project.json
│   │   └── tsconfig.json
│   │
│   ├── api-client/             # Typed API client for frontend
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── client.ts
│   │   │   └── types.ts
│   │   ├── project.json
│   │   └── tsconfig.json
│   │
│   ├── utils/                  # Generic utilities
│   │   ├── src/
│   │   │   ├── index.ts
│   │   │   ├── date.ts
│   │   │   └── string.ts
│   │   ├── project.json
│   │   └── tsconfig.json
│   │
│   └── config/                 # Shared configuration
│       ├── src/
│       │   ├── index.ts
│       │   └── constants.ts
│       ├── project.json
│       └── tsconfig.json
│
├── prisma/
│   ├── schema.prisma
│   ├── migrations/
│   └── seed.ts
│
├── .azuredevops/
│   └── pipelines/
│       ├── ci.yml
│       └── templates/
│           ├── install-dependencies.yml
│           └── setup-database.yml
│
├── azure-pipelines.yml          # Main pipeline entry point
│
├── .vscode/
│   ├── settings.json
│   ├── extensions.json
│   └── launch.json
│
├── .cursor/
│   └── rules/
│       └── derived-cursor-rules.mdc  # (preserve existing)
│
├── docs/                       # (preserve existing)
│   ├── vision.md
│   ├── requirements.md
│   ├── implementation-plan.md
│   └── prompts/
│       └── prompt-phase0.md
│
├── nx.json
├── package.json
├── pnpm-workspace.yaml
├── tsconfig.base.json
├── .gitignore
├── .env.example
├── .prettierrc
├── .eslintrc.json
└── README.md

🗄️ Database Schema (Prisma)

Schema Design Philosophy

The schema supports assemblies: a single Shopify product can consist of multiple 3D parts that need to be printed separately in SimplyPrint.

Key relationships: - ProductMapping (1) → AssemblyPart (many): One product can have multiple 3D files/parts - LineItem (1) → PrintJob (many): One order line item creates one print job per assembly part - PrintJob references which AssemblyPart it's printing

Shopify Product (SKU: "robot-kit")
    │
    └── ProductMapping
            │
            ├── AssemblyPart: "robot-body.gcode"      (part 1 of 3)
            ├── AssemblyPart: "robot-arm-left.gcode"  (part 2 of 3)
            └── AssemblyPart: "robot-arm-right.gcode" (part 3 of 3)
                    │
                    └── When ordered → 3 PrintJobs created

Create the following schema in prisma/schema.prisma:

// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// ============================================================================
// ENUMS
// ============================================================================

enum OrderStatus {
  PENDING
  PROCESSING
  PARTIALLY_COMPLETED  // Some parts printed, others pending
  COMPLETED
  FAILED
  CANCELLED
}

enum LineItemStatus {
  PENDING
  PRINTING            // At least one part is printing
  PARTIALLY_COMPLETED // Some parts done, others pending
  COMPLETED           // All parts printed
  FAILED
}

enum PrintJobStatus {
  QUEUED
  ASSIGNED
  PRINTING
  COMPLETED
  FAILED
  CANCELLED
}

enum EventSeverity {
  INFO
  WARNING
  ERROR
}

// ============================================================================
// ORDER MODELS
// ============================================================================

model Order {
  id                 String      @id @default(uuid())
  shopifyOrderId     String      @unique
  shopifyOrderNumber String
  status             OrderStatus @default(PENDING)
  customerName       String
  customerEmail      String?
  shippingAddress    Json
  totalPrice         Decimal     @db.Decimal(10, 2)
  currency           String      @default("EUR")

  // Fulfillment
  shopifyFulfillmentId String?
  trackingNumber       String?
  trackingUrl          String?

  // Counts for quick status checks
  totalParts           Int         @default(0)  // Total parts across all line items
  completedParts       Int         @default(0)  // Parts successfully printed

  // Timestamps
  createdAt          DateTime    @default(now())
  updatedAt          DateTime    @updatedAt
  completedAt        DateTime?

  // Relations
  lineItems          LineItem[]
  eventLogs          EventLog[]

  @@index([status])
  @@index([createdAt])
  @@index([shopifyOrderNumber])
}

model LineItem {
  id                 String         @id @default(uuid())
  orderId            String
  shopifyLineItemId  String
  productMappingId   String?        // Reference to product mapping used
  productSku         String
  productName        String
  variantTitle       String?
  quantity           Int            // Quantity ordered (each creates N print jobs per part)
  unitPrice          Decimal        @db.Decimal(10, 2)
  status             LineItemStatus @default(PENDING)

  // Assembly tracking
  totalParts         Int            @default(0)  // Number of parts in this product
  completedParts     Int            @default(0)  // Parts completed for this line item

  // Timestamps
  createdAt          DateTime       @default(now())
  updatedAt          DateTime       @updatedAt

  // Relations
  order              Order          @relation(fields: [orderId], references: [id], onDelete: Cascade)
  productMapping     ProductMapping? @relation(fields: [productMappingId], references: [id], onDelete: SetNull)
  printJobs          PrintJob[]     // One print job per assembly part (per quantity unit)

  @@unique([orderId, shopifyLineItemId])
  @@index([productSku])
  @@index([status])
}

// ============================================================================
// PRINT JOB MODELS
// ============================================================================

model PrintJob {
  id                 String         @id @default(uuid())
  lineItemId         String
  assemblyPartId     String?        // Which part of the assembly this job prints
  simplyPrintJobId   String?        @unique
  status             PrintJobStatus @default(QUEUED)

  // For quantity > 1: which copy is this? (1, 2, 3...)
  copyNumber         Int            @default(1)

  // Print details from SimplyPrint
  printerId          String?
  printerName        String?

  // File details (denormalized from AssemblyPart for historical record)
  fileId             String?
  fileName           String?

  // Timing
  queuedAt           DateTime       @default(now())
  startedAt          DateTime?
  completedAt        DateTime?
  estimatedDuration  Int?           // Estimated print time in seconds
  actualDuration     Int?           // Actual print time in seconds

  // Error handling
  errorMessage       String?
  retryCount         Int            @default(0)
  maxRetries         Int            @default(3)

  // Timestamps
  createdAt          DateTime       @default(now())
  updatedAt          DateTime       @updatedAt

  // Relations
  lineItem           LineItem       @relation(fields: [lineItemId], references: [id], onDelete: Cascade)
  assemblyPart       AssemblyPart?  @relation(fields: [assemblyPartId], references: [id], onDelete: SetNull)

  @@index([status])
  @@index([simplyPrintJobId])
  @@index([lineItemId])
  @@index([assemblyPartId])
}

// ============================================================================
// PRODUCT MAPPING MODELS
// ============================================================================

model ProductMapping {
  id                 String   @id @default(uuid())
  shopifyProductId   String
  shopifyVariantId   String?
  sku                String   @unique
  productName        String
  description        String?

  // Assembly configuration
  isAssembly         Boolean  @default(false)  // True if product has multiple parts

  // Default print profile (can be overridden per part)
  defaultPrintProfile Json?   // { material, quality, infill, supports, etc. }

  // Status
  isActive           Boolean  @default(true)

  // Timestamps
  createdAt          DateTime @default(now())
  updatedAt          DateTime @updatedAt

  // Relations
  assemblyParts      AssemblyPart[]  // Parts that make up this product
  lineItems          LineItem[]      // Line items that used this mapping

  @@index([shopifyProductId])
  @@index([isActive])
  @@index([sku])
}

model AssemblyPart {
  id                 String   @id @default(uuid())
  productMappingId   String

  // Part identification
  partName           String           // e.g., "Main Body", "Left Arm", "Right Arm"
  partNumber         Int      @default(1)  // Order/sequence number in assembly

  // SimplyPrint file reference
  simplyPrintFileId  String           // File ID in SimplyPrint
  simplyPrintFileName String?         // Human-readable filename

  // Print profile (overrides ProductMapping.defaultPrintProfile if set)
  printProfile       Json?            // { material, quality, infill, supports, etc. }

  // Part metadata
  estimatedPrintTime Int?             // Estimated print time in seconds
  estimatedFilament  Decimal?  @db.Decimal(10, 2)  // Estimated filament in grams

  // Quantity per product (e.g., need 2 of this part per product)
  quantityPerProduct Int      @default(1)

  // Status
  isActive           Boolean  @default(true)

  // Timestamps
  createdAt          DateTime @default(now())
  updatedAt          DateTime @updatedAt

  // Relations
  productMapping     ProductMapping @relation(fields: [productMappingId], references: [id], onDelete: Cascade)
  printJobs          PrintJob[]     // Print jobs for this part

  @@unique([productMappingId, partNumber])  // Part numbers unique within a product
  @@index([simplyPrintFileId])
  @@index([isActive])
}

// ============================================================================
// SYSTEM MODELS
// ============================================================================

model EventLog {
  id          String        @id @default(uuid())
  orderId     String?
  printJobId  String?       // Can also log events for specific print jobs
  eventType   String
  severity    EventSeverity @default(INFO)
  message     String
  metadata    Json?

  // Timestamps
  createdAt   DateTime      @default(now())

  // Relations
  order       Order?        @relation(fields: [orderId], references: [id], onDelete: SetNull)

  @@index([eventType])
  @@index([severity])
  @@index([createdAt])
  @@index([orderId])
  @@index([printJobId])
}

model SystemConfig {
  id          String   @id @default(uuid())
  key         String   @unique
  value       Json
  description String?

  // Timestamps
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}

// ============================================================================
// OPTIONAL: Printer tracking (for dashboard visibility)
// ============================================================================

model Printer {
  id                 String   @id @default(uuid())
  simplyPrintId      String   @unique  // Printer ID in SimplyPrint
  name               String
  model              String?

  // Status (cached from SimplyPrint)
  isOnline           Boolean  @default(false)
  currentStatus      String?  // idle, printing, error, etc.
  currentJobId       String?  // Currently printing job

  // Last sync
  lastSyncAt         DateTime @default(now())

  // Timestamps
  createdAt          DateTime @default(now())
  updatedAt          DateTime @updatedAt

  @@index([isOnline])
  @@index([currentStatus])
}

Schema Entity Relationship Diagram

uml diagram

uml diagram

Key Schema Features

Feature Description
Assembly Support One Shopify product can have multiple 3D parts via AssemblyPart
Quantity Handling copyNumber tracks which copy of ordered quantity this print is for
Part-per-Product quantityPerProduct allows parts that need multiple copies (e.g., 4 wheels per car)
Progress Tracking totalParts and completedParts on Order and LineItem for quick status
Partial Completion PARTIALLY_COMPLETED status when some parts done
Historical Record File details denormalized to PrintJob for audit trail
Printer Tracking Optional Printer model for dashboard visibility

🔧 Environment Configuration

.env.example

Create this file with ALL required environment variables:

# ============================================================================
# Forma3D.Connect Environment Configuration
# ============================================================================
# Copy this file to .env and fill in the values
# NEVER commit .env to version control
# ============================================================================

# ----------------------------------------------------------------------------
# Database
# ----------------------------------------------------------------------------
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/forma3d_connect?schema=public"

# ----------------------------------------------------------------------------
# Application
# ----------------------------------------------------------------------------
NODE_ENV="development"
APP_PORT=3000
APP_URL="http://localhost:3000"

# Frontend URL (for CORS)
FRONTEND_URL="http://localhost:4200"

# ----------------------------------------------------------------------------
# Shopify API
# ----------------------------------------------------------------------------
SHOPIFY_SHOP_DOMAIN="your-store.myshopify.com"
SHOPIFY_API_KEY="your-api-key"
SHOPIFY_API_SECRET="your-api-secret"
SHOPIFY_ACCESS_TOKEN="shpat_xxxxxxxxxxxxx"
SHOPIFY_WEBHOOK_SECRET="your-webhook-secret"
SHOPIFY_API_VERSION="2024-01"

# ----------------------------------------------------------------------------
# SimplyPrint API
# ----------------------------------------------------------------------------
SIMPLYPRINT_API_URL="https://api.simplyprint.io/v1"
SIMPLYPRINT_API_KEY="your-simplyprint-api-key"

# ----------------------------------------------------------------------------
# Sendcloud API
# ----------------------------------------------------------------------------
SENDCLOUD_PUBLIC_KEY="your-public-key"
SENDCLOUD_SECRET_KEY="your-secret-key"

# ----------------------------------------------------------------------------
# Optional: Logging
# ----------------------------------------------------------------------------
LOG_LEVEL="debug"

# ----------------------------------------------------------------------------
# Optional: Email Notifications
# ----------------------------------------------------------------------------
# SMTP_HOST=
# SMTP_PORT=
# SMTP_USER=
# SMTP_PASS=
# NOTIFICATION_EMAIL=

Environment Validation

Create strict environment validation in apps/api/src/config/env.validation.ts:

import { plainToInstance } from 'class-transformer';
import { IsEnum, IsNumber, IsString, IsUrl, validateSync } from 'class-validator';

enum Environment {
  Development = 'development',
  Production = 'production',
  Test = 'test',
}

export class EnvironmentVariables {
  @IsEnum(Environment)
  NODE_ENV: Environment;

  @IsNumber()
  APP_PORT: number;

  @IsUrl({ require_tld: false })
  APP_URL: string;

  @IsUrl({ require_tld: false })
  FRONTEND_URL: string;

  @IsString()
  DATABASE_URL: string;

  @IsString()
  SHOPIFY_SHOP_DOMAIN: string;

  @IsString()
  SHOPIFY_API_KEY: string;

  @IsString()
  SHOPIFY_API_SECRET: string;

  @IsString()
  SHOPIFY_ACCESS_TOKEN: string;

  @IsString()
  SHOPIFY_WEBHOOK_SECRET: string;

  @IsString()
  SHOPIFY_API_VERSION: string;

  @IsUrl()
  SIMPLYPRINT_API_URL: string;

  @IsString()
  SIMPLYPRINT_API_KEY: string;

  @IsString()
  SENDCLOUD_PUBLIC_KEY: string;

  @IsString()
  SENDCLOUD_SECRET_KEY: string;
}

export function validate(config: Record<string, unknown>) {
  const validatedConfig = plainToInstance(EnvironmentVariables, config, {
    enableImplicitConversion: true,
  });
  const errors = validateSync(validatedConfig, {
    skipMissingProperties: false,
  });

  if (errors.length > 0) {
    throw new Error(errors.toString());
  }
  return validatedConfig;
}

🚀 CI/CD Pipeline (Azure DevOps)

azure-pipelines.yml (Root Pipeline Entry Point)

# ============================================================================
# Forma3D.Connect - Azure DevOps CI/CD Pipeline
# ============================================================================
# This is the main pipeline entry point
# ============================================================================

trigger:
  branches:
    include:
      - main
      - develop
  paths:
    exclude:
      - '*.md'
      - 'docs/**'

pr:
  branches:
    include:
      - main
      - develop

variables:
  nodeVersion: '20.x'
  pnpmVersion: '9'
  isMain: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')]
  isDevelop: $[eq(variables['Build.SourceBranch'], 'refs/heads/develop')]

pool:
  vmImage: 'ubuntu-latest'

stages:
  - stage: Validate
    displayName: 'Validate'
    jobs:
      - job: Lint
        displayName: 'Lint'
        steps:
          - template: .azuredevops/pipelines/templates/install-dependencies.yml
          - script: pnpm nx affected --target=lint --parallel=3
            displayName: 'Run Linting'

      - job: TypeCheck
        displayName: 'Type Check'
        steps:
          - template: .azuredevops/pipelines/templates/install-dependencies.yml
          - script: pnpm prisma generate
            displayName: 'Generate Prisma Client'
          - script: pnpm nx run-many --target=typecheck --all --parallel=3
            displayName: 'Run Type Check'

  - stage: Test
    displayName: 'Test'
    dependsOn: Validate
    jobs:
      - job: UnitTests
        displayName: 'Unit Tests'
        services:
          postgres:
            image: postgres:16
            ports:
              - 5432:5432
            env:
              POSTGRES_USER: postgres
              POSTGRES_PASSWORD: postgres
              POSTGRES_DB: forma3d_connect_test
        steps:
          - template: .azuredevops/pipelines/templates/install-dependencies.yml
          - template: .azuredevops/pipelines/templates/setup-database.yml
          - script: pnpm nx affected --target=test --parallel=3 --coverage
            displayName: 'Run Unit Tests'
            env:
              DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/forma3d_connect_test?schema=public'
          - task: PublishTestResults@2
            displayName: 'Publish Test Results'
            condition: succeededOrFailed()
            inputs:
              testResultsFormat: 'JUnit'
              testResultsFiles: '**/junit.xml'
              mergeTestResults: true
          - task: PublishCodeCoverageResults@1
            displayName: 'Publish Code Coverage'
            condition: succeededOrFailed()
            inputs:
              codeCoverageTool: 'Cobertura'
              summaryFileLocation: '$(System.DefaultWorkingDirectory)/coverage/**/cobertura-coverage.xml'

  - stage: Build
    displayName: 'Build'
    dependsOn: Test
    jobs:
      - job: BuildAll
        displayName: 'Build All Projects'
        steps:
          - template: .azuredevops/pipelines/templates/install-dependencies.yml
          - script: pnpm prisma generate
            displayName: 'Generate Prisma Client'
          - script: pnpm nx affected --target=build --parallel=3
            displayName: 'Build Projects'
          - task: PublishBuildArtifacts@1
            displayName: 'Publish API Artifact'
            inputs:
              pathToPublish: 'dist/apps/api'
              artifactName: 'api'
            condition: and(succeeded(), or(eq(variables.isMain, true), eq(variables.isDevelop, true)))
          - task: PublishBuildArtifacts@1
            displayName: 'Publish Web Artifact'
            inputs:
              pathToPublish: 'dist/apps/web'
              artifactName: 'web'
            condition: and(succeeded(), or(eq(variables.isMain, true), eq(variables.isDevelop, true)))

  - stage: DeployStaging
    displayName: 'Deploy to Staging'
    dependsOn: Build
    condition: and(succeeded(), eq(variables.isDevelop, true))
    jobs:
      - deployment: DeployStaging
        displayName: 'Deploy to Staging'
        environment: 'staging'
        strategy:
          runOnce:
            deploy:
              steps:
                - script: echo "Deploy to staging environment"
                  displayName: 'Deploy Staging (placeholder)'

  - stage: DeployProduction
    displayName: 'Deploy to Production'
    dependsOn: Build
    condition: and(succeeded(), eq(variables.isMain, true))
    jobs:
      - deployment: DeployProduction
        displayName: 'Deploy to Production'
        environment: 'production'
        strategy:
          runOnce:
            deploy:
              steps:
                - script: echo "Deploy to production environment"
                  displayName: 'Deploy Production (placeholder)'

.azuredevops/pipelines/templates/install-dependencies.yml

# ============================================================================
# Template: Install Dependencies
# ============================================================================
# Reusable template for installing Node.js, pnpm, and project dependencies
# ============================================================================

steps:
  - task: NodeTool@0
    displayName: 'Install Node.js'
    inputs:
      versionSpec: '$(nodeVersion)'

  - script: |
      corepack enable
      corepack prepare pnpm@$(pnpmVersion) --activate
    displayName: 'Install pnpm'

  - task: Cache@2
    displayName: 'Cache pnpm store'
    inputs:
      key: 'pnpm | "$(Agent.OS)" | pnpm-lock.yaml'
      restoreKeys: |
        pnpm | "$(Agent.OS)"
      path: $(Pipeline.Workspace)/.pnpm-store

  - script: |
      pnpm config set store-dir $(Pipeline.Workspace)/.pnpm-store
      pnpm install --frozen-lockfile
    displayName: 'Install Dependencies'

  - script: |
      git fetch origin main:main --depth=1 || true
    displayName: 'Fetch main for Nx affected'
    condition: ne(variables['Build.SourceBranch'], 'refs/heads/main')

.azuredevops/pipelines/templates/setup-database.yml

# ============================================================================
# Template: Setup Database
# ============================================================================
# Reusable template for setting up the test database
# ============================================================================

steps:
  - script: |
      echo "Waiting for PostgreSQL to be ready..."
      for i in {1..30}; do
        pg_isready -h localhost -p 5432 -U postgres && break
        sleep 1
      done
    displayName: 'Wait for PostgreSQL'

  - script: pnpm prisma generate
    displayName: 'Generate Prisma Client'

  - script: pnpm prisma migrate deploy
    displayName: 'Run Database Migrations'
    env:
      DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/forma3d_connect_test?schema=public'

.azuredevops/pipelines/ci.yml (Alternative standalone CI file)

# ============================================================================
# Forma3D.Connect - CI Pipeline (Standalone)
# ============================================================================
# Use this if you prefer a simpler single-file pipeline
# ============================================================================

trigger:
  - main
  - develop

pr:
  - main
  - develop

pool:
  vmImage: 'ubuntu-latest'

variables:
  nodeVersion: '20.x'
  pnpmVersion: '9'

stages:
  - stage: CI
    displayName: 'Continuous Integration'
    jobs:
      # -----------------------------------------------------------------------
      # Lint Job
      # -----------------------------------------------------------------------
      - job: Lint
        displayName: 'Lint'
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: '$(nodeVersion)'
            displayName: 'Install Node.js'

          - script: |
              corepack enable
              corepack prepare pnpm@$(pnpmVersion) --activate
            displayName: 'Install pnpm'

          - script: pnpm install --frozen-lockfile
            displayName: 'Install Dependencies'

          - script: pnpm nx affected --target=lint --parallel=3
            displayName: 'Run Linting'

      # -----------------------------------------------------------------------
      # Test Job
      # -----------------------------------------------------------------------
      - job: Test
        displayName: 'Test'
        services:
          postgres:
            image: postgres:16
            ports:
              - 5432:5432
            env:
              POSTGRES_USER: postgres
              POSTGRES_PASSWORD: postgres
              POSTGRES_DB: forma3d_connect_test
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: '$(nodeVersion)'
            displayName: 'Install Node.js'

          - script: |
              corepack enable
              corepack prepare pnpm@$(pnpmVersion) --activate
            displayName: 'Install pnpm'

          - script: pnpm install --frozen-lockfile
            displayName: 'Install Dependencies'

          - script: pnpm prisma generate
            displayName: 'Generate Prisma Client'

          - script: |
              for i in {1..30}; do
                pg_isready -h localhost -p 5432 -U postgres && break
                sleep 1
              done
            displayName: 'Wait for PostgreSQL'

          - script: pnpm prisma migrate deploy
            displayName: 'Run Migrations'
            env:
              DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/forma3d_connect_test?schema=public'

          - script: pnpm nx affected --target=test --parallel=3 --coverage
            displayName: 'Run Tests'
            env:
              DATABASE_URL: 'postgresql://postgres:postgres@localhost:5432/forma3d_connect_test?schema=public'

      # -----------------------------------------------------------------------
      # Build Job
      # -----------------------------------------------------------------------
      - job: Build
        displayName: 'Build'
        dependsOn:
          - Lint
          - Test
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: '$(nodeVersion)'
            displayName: 'Install Node.js'

          - script: |
              corepack enable
              corepack prepare pnpm@$(pnpmVersion) --activate
            displayName: 'Install pnpm'

          - script: pnpm install --frozen-lockfile
            displayName: 'Install Dependencies'

          - script: pnpm prisma generate
            displayName: 'Generate Prisma Client'

          - script: pnpm nx affected --target=build --parallel=3
            displayName: 'Build Projects'

      # -----------------------------------------------------------------------
      # Type Check Job
      # -----------------------------------------------------------------------
      - job: TypeCheck
        displayName: 'Type Check'
        steps:
          - task: NodeTool@0
            inputs:
              versionSpec: '$(nodeVersion)'
            displayName: 'Install Node.js'

          - script: |
              corepack enable
              corepack prepare pnpm@$(pnpmVersion) --activate
            displayName: 'Install pnpm'

          - script: pnpm install --frozen-lockfile
            displayName: 'Install Dependencies'

          - script: pnpm prisma generate
            displayName: 'Generate Prisma Client'

          - script: pnpm nx run-many --target=typecheck --all --parallel=3
            displayName: 'Run Type Check'

📦 Root Configuration Files

package.json

{
  "name": "forma3d-connect",
  "version": "0.0.1",
  "private": true,
  "description": "Integration services connecting Shopify and SimplyPrint for Forma 3D print farm",
  "license": "UNLICENSED",
  "scripts": {
    "start": "nx serve",
    "build": "nx build",
    "test": "nx test",
    "lint": "nx lint",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "prepare": "prisma generate",
    "db:migrate": "prisma migrate dev",
    "db:migrate:deploy": "prisma migrate deploy",
    "db:seed": "prisma db seed",
    "db:studio": "prisma studio",
    "api:dev": "nx serve api",
    "web:dev": "nx serve web",
    "dev": "nx run-many --target=serve --projects=api,web --parallel"
  },
  "prisma": {
    "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
  },
  "engines": {
    "node": ">=20.0.0",
    "pnpm": ">=9.0.0"
  },
  "packageManager": "pnpm@9.0.0"
}

nx.json

{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "defaultBase": "main",
  "namedInputs": {
    "default": ["{projectRoot}/**/*", "sharedGlobals"],
    "production": [
      "default",
      "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
      "!{projectRoot}/tsconfig.spec.json",
      "!{projectRoot}/jest.config.[jt]s",
      "!{projectRoot}/.eslintrc.json"
    ],
    "sharedGlobals": ["{workspaceRoot}/tsconfig.base.json"]
  },
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["production", "^production"],
      "cache": true
    },
    "lint": {
      "inputs": ["default", "{workspaceRoot}/.eslintrc.json"],
      "cache": true
    },
    "test": {
      "inputs": ["default", "^default"],
      "cache": true
    },
    "typecheck": {
      "cache": true
    }
  },
  "plugins": [
    {
      "plugin": "@nx/vite/plugin",
      "options": {
        "buildTargetName": "build",
        "serveTargetName": "serve",
        "previewTargetName": "preview",
        "testTargetName": "test"
      }
    },
    {
      "plugin": "@nx/eslint/plugin",
      "options": {
        "targetName": "lint"
      }
    }
  ],
  "generators": {
    "@nx/react": {
      "application": {
        "style": "css",
        "linter": "eslint",
        "bundler": "vite"
      },
      "component": {
        "style": "css"
      },
      "library": {
        "style": "css",
        "linter": "eslint"
      }
    }
  }
}

tsconfig.base.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": ".",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "ES2022",
    "module": "ESNext",
    "lib": ["ES2022", "DOM"],
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "forceConsistentCasingInFileNames": true,
    "baseUrl": ".",
    "paths": {
      "@forma3d/domain": ["libs/domain/src/index.ts"],
      "@forma3d/api-client": ["libs/api-client/src/index.ts"],
      "@forma3d/utils": ["libs/utils/src/index.ts"],
      "@forma3d/config": ["libs/config/src/index.ts"]
    }
  },
  "exclude": ["node_modules", "tmp"]
}

.prettierrc

{
  "singleQuote": true,
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": true,
  "printWidth": 100,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf"
}

.eslintrc.json

{
  "root": true,
  "ignorePatterns": ["**/*"],
  "plugins": ["@nx"],
  "overrides": [
    {
      "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
      "rules": {
        "@nx/enforce-module-boundaries": [
          "error",
          {
            "enforceBuildableLibDependency": true,
            "allow": [],
            "depConstraints": [
              {
                "sourceTag": "scope:api",
                "onlyDependOnLibsWithTags": ["scope:shared"]
              },
              {
                "sourceTag": "scope:web",
                "onlyDependOnLibsWithTags": ["scope:shared"]
              },
              {
                "sourceTag": "scope:shared",
                "onlyDependOnLibsWithTags": ["scope:shared"]
              }
            ]
          }
        ]
      }
    },
    {
      "files": ["*.ts", "*.tsx"],
      "extends": ["plugin:@nx/typescript"],
      "rules": {
        "@typescript-eslint/no-explicit-any": "error",
        "@typescript-eslint/explicit-function-return-type": "off",
        "@typescript-eslint/explicit-module-boundary-types": "off",
        "@typescript-eslint/no-unused-vars": [
          "error",
          { "argsIgnorePattern": "^_" }
        ]
      }
    },
    {
      "files": ["*.js", "*.jsx"],
      "extends": ["plugin:@nx/javascript"],
      "rules": {}
    }
  ]
}

.gitignore

# ============================================================================
# Forma3D.Connect .gitignore
# ============================================================================

# ----------------------------------------------------------------------------
# Dependencies
# ----------------------------------------------------------------------------
node_modules/
.pnpm-store/

# ----------------------------------------------------------------------------
# Build outputs
# ----------------------------------------------------------------------------
dist/
build/
out/
.next/
.nuxt/

# ----------------------------------------------------------------------------
# Nx
# ----------------------------------------------------------------------------
.nx/
.nx/cache
.nx/workspace-data

# ----------------------------------------------------------------------------
# TypeScript
# ----------------------------------------------------------------------------
*.tsbuildinfo

# ----------------------------------------------------------------------------
# Testing
# ----------------------------------------------------------------------------
coverage/
.nyc_output/

# ----------------------------------------------------------------------------
# Environment
# ----------------------------------------------------------------------------
.env
.env.local
.env.*.local
!.env.example
!.env.template

# ----------------------------------------------------------------------------
# IDE / Editor
# ----------------------------------------------------------------------------
.idea/
*.swp
*.swo
*~

# VS Code - ignore all except shared configs
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json
!.vscode/launch.json
!.vscode/tasks.json

# ----------------------------------------------------------------------------
# Cursor AI
# ----------------------------------------------------------------------------
.cursor/*
!.cursor/rules/

# ----------------------------------------------------------------------------
# OS Files
# ----------------------------------------------------------------------------
.DS_Store
Thumbs.db
*.log

# ----------------------------------------------------------------------------
# Prisma
# ----------------------------------------------------------------------------
# Keep migrations, ignore generated client (it's generated on install)

# ----------------------------------------------------------------------------
# Vite
# ----------------------------------------------------------------------------
vite.config.ts.timestamp-*

# ----------------------------------------------------------------------------
# Tauri (future)
# ----------------------------------------------------------------------------
apps/desktop/src-tauri/target/

# ----------------------------------------------------------------------------
# Capacitor (future)
# ----------------------------------------------------------------------------
apps/mobile/android/
apps/mobile/ios/

# ----------------------------------------------------------------------------
# Temporary
# ----------------------------------------------------------------------------
tmp/
temp/
*.tmp

📝 README.md

Create a comprehensive README with detailed build, test, and run instructions:

# Forma3D.Connect

> Integration services connecting Shopify e-commerce with SimplyPrint 3D print farm management

[![Build Status](https://dev.azure.com/YOUR_ORG/forma-3d-connect/_apis/build/status/CI?branchName=main)](https://dev.azure.com/YOUR_ORG/forma-3d-connect/_build/latest?definitionId=1&branchName=main)

## Overview

Forma3D.Connect automates the print-on-demand workflow for Forma 3D's 3D printing operations:

1. **Order Reception**: Shopify orders are received via webhooks
2. **Print Job Creation**: Orders automatically create print jobs in SimplyPrint
3. **Status Tracking**: Print job status is monitored and synced
4. **Order Fulfillment**: Completed prints trigger Shopify fulfillment
5. **Shipping**: Sendcloud generates shipping labels

---

## Tech Stack

| Layer | Technology | Version |
|-------|------------|---------|
| Frontend | React, TypeScript, Vite, Tailwind CSS | 19.x |
| Backend | NestJS, TypeScript, Socket.IO | 10.x |
| Database | PostgreSQL, Prisma ORM | 16.x / 5.x |
| Monorepo | Nx | 19.x |
| Package Manager | pnpm | 9.x |
| CI/CD | Azure DevOps Pipelines | - |

---

## Prerequisites

Before you begin, ensure you have the following installed:

| Tool | Version | Installation |
|------|---------|--------------|
| Node.js | 20.x LTS | [nodejs.org](https://nodejs.org/) or use nvm |
| pnpm | 9.x | `corepack enable && corepack prepare pnpm@9 --activate` |
| PostgreSQL | 16.x | [postgresql.org](https://www.postgresql.org/download/) or Docker |
| Docker | Latest | [docker.com](https://www.docker.com/) (optional) |
| Git | Latest | [git-scm.com](https://git-scm.com/) |

### Verify Prerequisites

```bash
# Check Node.js version
node --version  # Should be v20.x.x

# Check pnpm version
pnpm --version  # Should be 9.x.x

# Check PostgreSQL (if installed locally)
psql --version  # Should be 16.x

# Check Docker (if using containerized database)
docker --version

Getting Started

1. Clone the Repository

# Clone via HTTPS
git clone https://YOUR_ORG@dev.azure.com/YOUR_ORG/forma-3d-connect/_git/forma-3d-connect

# Or clone via SSH
git clone git@ssh.dev.azure.com:v3/YOUR_ORG/forma-3d-connect/forma-3d-connect

# Navigate to project directory
cd forma-3d-connect

2. Install Dependencies

# Install all dependencies (this also generates Prisma client)
pnpm install

3. Environment Configuration

# Copy the example environment file
cp .env.example .env

# Edit the .env file with your configuration
# Required variables:
# - DATABASE_URL: PostgreSQL connection string
# - SHOPIFY_* : Shopify API credentials
# - SIMPLYPRINT_* : SimplyPrint API credentials
# - SENDCLOUD_* : Sendcloud API credentials

Minimum Required Environment Variables for Development:

DATABASE_URL="postgresql://postgres:postgres@localhost:5432/forma3d_connect?schema=public"
NODE_ENV="development"
APP_PORT=3000
APP_URL="http://localhost:3000"
FRONTEND_URL="http://localhost:4200"

4. Database Setup

Choose one of the following options:

# Start PostgreSQL container
docker run --name forma3d-postgres \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=forma3d_connect \
  -p 5432:5432 \
  -d postgres:16

# Verify container is running
docker ps

# Run database migrations
pnpm db:migrate

# (Optional) Seed the database with test data
pnpm db:seed

Option B: Local PostgreSQL Installation

# Create the database
createdb forma3d_connect

# Run database migrations
pnpm db:migrate

# (Optional) Seed the database with test data
pnpm db:seed

Option C: Docker Compose (Full Stack)

# Create docker-compose.yml for local development
# (See Infrastructure section below for full compose file)

docker-compose up -d

pnpm db:migrate

5. Start Development Servers

# Start both API and Web simultaneously
pnpm dev

# API will be available at: http://localhost:3000
# Web will be available at: http://localhost:4200

Or start services individually:

# Terminal 1: Start Backend API
pnpm api:dev

# Terminal 2: Start Frontend Web
pnpm web:dev

6. Verify Installation

# Test API health endpoint
curl http://localhost:3000/health
# Expected: {"status":"ok","database":"connected","timestamp":"..."}

# Open frontend in browser
open http://localhost:4200
# Expected: Dashboard with "System Health: OK" indicator

Building the Application

Development Build

# Build all projects
pnpm build

# Build specific project
pnpm nx build api
pnpm nx build web

Production Build

# Build for production with optimizations
NODE_ENV=production pnpm build

# Build outputs are in:
# - dist/apps/api    (NestJS backend)
# - dist/apps/web    (React frontend)

Build Specific Libraries

# Build shared domain library
pnpm nx build domain

# Build all libraries
pnpm nx run-many --target=build --projects=domain,api-client,utils,config

Testing

Run All Tests

# Run all tests across the monorepo
pnpm test

# Run tests with coverage
pnpm test -- --coverage

Run Tests by Project

# Test backend API
pnpm nx test api

# Test frontend Web
pnpm nx test web

# Test shared libraries
pnpm nx test domain
pnpm nx test utils

Run Tests in Watch Mode

# Watch mode for specific project
pnpm nx test api --watch

# Watch mode for affected projects
pnpm nx affected --target=test --watch

Run E2E Tests

# Run end-to-end tests (requires running application)
pnpm nx e2e api-e2e

Test Coverage Report

# Generate coverage report
pnpm test -- --coverage

# Coverage reports are generated in:
# - coverage/apps/api/
# - coverage/apps/web/
# - coverage/libs/*/

Linting and Code Quality

Run Linting

# Lint all projects
pnpm lint

# Lint specific project
pnpm nx lint api
pnpm nx lint web

# Lint only affected projects
pnpm nx affected --target=lint

Fix Linting Errors Automatically

# Auto-fix linting issues
pnpm nx lint api --fix
pnpm nx lint web --fix

Format Code

# Check formatting
pnpm format:check

# Fix formatting
pnpm format

Type Checking

# Run TypeScript type checking
pnpm nx run-many --target=typecheck --all

# Type check specific project
pnpm nx typecheck api

Database Management

Migrations

# Create a new migration (after schema changes)
pnpm db:migrate

# Apply migrations to database
pnpm db:migrate:deploy

# Reset database (drop, create, migrate, seed)
pnpm prisma migrate reset

Prisma Studio

# Open Prisma Studio (visual database browser)
pnpm db:studio
# Opens at http://localhost:5555

Database Seeding

# Seed database with test data
pnpm db:seed

Generate Prisma Client

# Regenerate Prisma client after schema changes
pnpm prisma generate

Running in Production

Build for Production

# Build all projects for production
NODE_ENV=production pnpm build

Run Production API

# Navigate to API dist folder
cd dist/apps/api

# Start with Node.js
node main.js

# Or use PM2 for process management
pm2 start main.js --name forma3d-api

Serve Production Frontend

The frontend build output (dist/apps/web) is a static site that can be served by:

  • Nginx
  • Azure Static Web Apps
  • Any CDN/static hosting service

Example Nginx configuration:

server {
    listen 80;
    server_name forma3d.example.com;
    root /var/www/forma3d/web;
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /api {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}

Infrastructure Setup

Docker Compose (Development)

Create docker-compose.yml in project root:

version: '3.8'

services:
  postgres:
    image: postgres:16
    container_name: forma3d-postgres
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: forma3d_connect
    ports:
      - '5432:5432'
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U postgres']
      interval: 10s
      timeout: 5s
      retries: 5

  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
    container_name: forma3d-api
    environment:
      DATABASE_URL: postgresql://postgres:postgres@postgres:5432/forma3d_connect?schema=public
      NODE_ENV: development
    ports:
      - '3000:3000'
    depends_on:
      postgres:
        condition: service_healthy

  web:
    build:
      context: .
      dockerfile: apps/web/Dockerfile
    container_name: forma3d-web
    ports:
      - '4200:80'
    depends_on:
      - api

volumes:
  postgres_data:

Azure DevOps Setup

  1. Create Pipeline:
  2. Go to Azure DevOps > Pipelines > New Pipeline
  3. Select your repository
  4. Choose "Existing Azure Pipelines YAML file"
  5. Select azure-pipelines.yml

  6. Configure Environments:

  7. Create staging and production environments in Azure DevOps
  8. Add approval gates for production deployments

  9. Add Service Connections:

  10. Configure Azure service connection for deployments
  11. Add any required variable groups for secrets

Project Structure

forma-3d-connect/
├── apps/
│   ├── api/                    # NestJS backend application
│   │   ├── src/
│   │   │   ├── config/         # Configuration module
│   │   │   ├── database/       # Prisma service
│   │   │   ├── health/         # Health check endpoints
│   │   │   └── main.ts         # Application entry point
│   │   └── test/               # E2E tests
│   │
│   └── web/                    # React 19 frontend application
│       ├── src/
│       │   ├── components/     # React components
│       │   ├── hooks/          # Custom hooks
│       │   ├── pages/          # Page components
│       │   └── lib/            # Utilities
│       └── public/             # Static assets
│
├── libs/
│   ├── domain/                 # Shared types, entities, enums
│   ├── api-client/             # Typed API client for frontend
│   ├── utils/                  # Generic utilities
│   └── config/                 # Shared configuration
│
├── prisma/
│   ├── schema.prisma           # Database schema
│   ├── migrations/             # Database migrations
│   └── seed.ts                 # Seed script
│
├── .azuredevops/
│   └── pipelines/              # Azure DevOps pipeline templates
│
├── docs/                       # Project documentation
│
├── azure-pipelines.yml         # Main CI/CD pipeline
├── nx.json                     # Nx workspace configuration
├── package.json                # Root package.json
├── tsconfig.base.json          # Base TypeScript configuration
└── .env.example                # Environment variables template

Available Scripts

Script Description
pnpm dev Start API and Web in development mode
pnpm api:dev Start only the API in development mode
pnpm web:dev Start only the Web in development mode
pnpm build Build all projects
pnpm test Run all tests
pnpm lint Lint all projects
pnpm format Format all files with Prettier
pnpm format:check Check formatting without fixing
pnpm db:migrate Create and run database migrations
pnpm db:migrate:deploy Apply migrations to database
pnpm db:seed Seed the database with test data
pnpm db:studio Open Prisma Studio

Nx Commands

# View project graph
pnpm nx graph

# Run affected tests
pnpm nx affected --target=test

# Run affected builds
pnpm nx affected --target=build

# List all projects
pnpm nx show projects

# View project details
pnpm nx show project api

Troubleshooting

Common Issues

1. Database Connection Failed

# Check if PostgreSQL is running
docker ps  # or: systemctl status postgresql

# Verify connection string in .env
echo $DATABASE_URL

# Test connection
psql $DATABASE_URL -c "SELECT 1"

2. Port Already in Use

# Find process using port 3000
lsof -i :3000

# Kill the process
kill -9 <PID>

3. Prisma Client Not Generated

# Regenerate Prisma client
pnpm prisma generate

4. Node Modules Issues

# Clean install
rm -rf node_modules
pnpm install

5. Nx Cache Issues

# Clear Nx cache
pnpm nx reset

Documentation


Contributing

  1. Create a feature branch from develop
  2. Make your changes
  3. Ensure all tests pass: pnpm test
  4. Ensure linting passes: pnpm lint
  5. Create a pull request to develop

License

UNLICENSED - Proprietary

---

## ✅ Validation Checklist

After completing Phase 0, verify ALL of the following:

### Infrastructure
- [x] `pnpm install` completes without errors ✅
- [x] `pnpm nx graph` shows correct project dependencies ✅
- [x] All path aliases resolve correctly (`@forma3d/domain`, etc.) ✅

### Backend (API)
- [x] `pnpm nx serve api` starts NestJS on port 3000 ✅
- [x] `GET http://localhost:3000/health` returns `{ status: 'ok' }` ✅
- [x] Environment variables validated on startup ✅
- [x] Prisma Client connects to database ✅

### Frontend (Web)
- [x] `pnpm nx serve web` starts Vite on port 4200 ✅
- [x] Dashboard page loads with basic layout ✅
- [x] Health check hook fetches from API ✅

### Database
- [x] `pnpm db:migrate` creates all tables ✅
- [x] `pnpm db:studio` opens Prisma Studio ✅
- [x] All 8 tables exist: Order, LineItem, PrintJob, ProductMapping, AssemblyPart, EventLog, SystemConfig, Printer ✅

### Quality
- [x] `pnpm lint` passes with no errors ✅
- [x] `pnpm test` passes (at least placeholder tests) ✅
- [x] `pnpm build` completes successfully ✅
- [x] TypeScript strict mode enabled (no `any` types) ✅

### CI/CD (Azure DevOps)
- [x] `azure-pipelines.yml` exists in root ✅
- [ ] `.azuredevops/pipelines/` folder with templates exists (using simplified pipeline)
- [x] Pipeline YAML is valid and would pass (lint, test, build stages) ✅

**Status: Phase 0 COMPLETE** ✅ (Completed January 2026)

---

## 🚫 Constraints and Rules

### MUST DO
- Use TypeScript strict mode everywhere
- Use exact versions from tech stack
- Follow Nx project boundaries
- Keep files under 300 lines
- Write explicit types (no inference for function returns)
- Use DTOs for all API inputs/outputs
- Log all errors with context

### MUST NOT
- Use `any` type
- Use `@ts-ignore` or `eslint-disable`
- Put Prisma calls outside repository layer
- Import `apps` from `libs`
- Mix frontend and backend code
- Console.log in production paths
- Store secrets in code

---

## 🎬 Execution Order

Execute tasks in this order:

1. **Initialize Nx workspace** with pnpm
2. **Create apps/api** (NestJS)
3. **Create apps/web** (React + Vite)
4. **Create libs/domain** (types)
5. **Create libs/api-client**
6. **Create libs/utils**
7. **Create libs/config**
8. **Set up Prisma** with schema
9. **Configure environment** validation
10. **Create health endpoints** (API + frontend hook)
11. **Configure ESLint/Prettier**
12. **Create Azure DevOps pipeline** (`azure-pipelines.yml` + templates)
13. **Write comprehensive README** (with build/test/run instructions)
14. **Run validation checklist**

---

## 📊 Expected Output

When Phase 0 is complete, you should be able to:

```bash
# Install dependencies
pnpm install

# Start development
pnpm dev

# In another terminal, test health
curl http://localhost:3000/health
# Returns: {"status":"ok","database":"connected","timestamp":"..."}

# Open frontend
open http://localhost:4200
# Shows: Dashboard with "System Health: OK" indicator

# Run all checks
pnpm lint && pnpm test && pnpm build
# All pass


END OF PROMPT


This prompt is designed for AI-first development. The AI should execute all steps and produce a complete, working foundation that passes all validation criteria.