Skip to content

Checkout JavaScript Widget

SmartCut Checkout is an embedded JavaScript widget that provides a complete cut calculator for your e-commerce or quotation website.

Overview

SmartCut Checkout allows you to:

  • Embed a cutting calculator directly on your website
  • Calculate prices in real-time on the client side
  • Filter products in your catalogue
  • Customize the UI to match your brand
  • Support multiple materials, extras, and configurations
  • No server-side integration required

Quick Start

Installation

Include the script and default css styling in your HTML:

html
<link rel="stylesheet" href="https://cutlistevo.com/checkout/checkout.css" />
<script type="module" src="https://cutlistevo.com/checkout/checkout.js"></script>

Basic Usage

html
<!-- Container for the calculator -->
<div id="smartcut-app"></div>

<script>
// Configuration
const initData = {
  stock: [
    {
      l: 2440,
      w: 1220,
      t: 18,
      cost: 65,
      material: 'Plywood'
    }
  ],
  saw: {
    stockType: 'sheet',
    bladeWidth: 3.2,
    cutType: 'efficiency',
    cutPreference: 'l'
  },
  options: {
    locale: 'en-US',
    currency: 'USD'
  }
}

// Wait for the ready event and then call init
window.addEventListener('smartcut/ready', () => {
  window.smartcutCheckout.init(initData)
})

// Listen for calculation events
window.addEventListener('smartcut/calculating', () => {
  console.log('Calculation started')
})

// Listen for results
window.addEventListener('smartcut/result', (e) => {
  const result = e.detail
  console.log('Optimization complete:', result)
  console.log('Total stock cost:', result.checkout.formattedTotalStockCost)
})
</script>

Configuration

Stock Configuration

Define the available stock materials:

javascript
const initData = {
  stock: [
    {
      l: 2440,
      w: 1220,
      t: 18,
      cost: 65,
      material: 'Plywood'
    }
  ],
  saw: {
    stockType: 'sheet',
    bladeWidth: 3.2,
    cutPreference: 'flex'
  },
  options: {
    locale: 'en-US',
    currency: 'USD'
  }
}

Saw Options

Saw configuration should be provided in the saw property (recommended) or in options for backward compatibility.

Complete Saw Configuration

javascript
saw: {
  stockType: 'sheet', // 'sheet', 'linear', or 'roll'
  bladeWidth: 3.2, // Blade width in mm
  cutType: 'guillotine', // 'efficiency', 'guillotine', 'beam', 'none'
  cutPreference: 'flex', // 'l' (length), 'w' (width), 'flex' (flexible), 'none'
  stackHeight: 60, // Maximum stack height in mm (optional)
  guillotineOptions: { // Optional guillotine settings
    headCuts: true,
    strategy: 'efficiency', // 'efficiency' or 'time'
    maxPhase: 3
  }
}

Cut Type Values:

  • 'efficiency' - Standard efficiency optimization
  • 'guillotine' - Guillotine cutting (only straight cuts across entire stock)
  • 'beam' - Beam saw optimization

Cut Preference Values:

  • 'l' - Prefer length-first cuts
  • 'w' - Prefer width-first cuts

Configuration Options

Number Format

javascript
options: {
  numberFormat: 'decimal', // 'decimal' or 'fraction'
  decimalPlaces: 2
}

Orientation Model

javascript
options: {
  orientationModel: 0 // 0, 1, or 2
}

Custom Fields

javascript
options: {
  customFields: [
    {
      type: 'string',
      label: 'Project Name',
      id: 'projectName',
      placeholder: 'Enter project name'
    },
    {
      type: 'integer',
      label: 'Quantity',
      id: 'quantity',
      default: 1
    },
    {
      type: 'select',
      label: 'Finish',
      id: 'finish',
      outputType: 'string',
      options: [
        { label: 'Matt', value: 'matt' },
        { label: 'Gloss', value: 'gloss' }
      ]
    },
    {
      type: 'checkbox',
      label: 'Rush Order',
      id: 'rush',
      trueValue: 'yes',
      falseValue: 'no',
      default: 'no'
    }
  ]
}

UI Customization

Control colors, enabled features, and field order:

javascript
options: {
  enable: {
    banding: true,
    finish: true,
    planing: true,
    machining: true,
    diagram: true,
    orientation: true,
    csvImport: false,
    partName: true,
    focus: false,
    click: true
  },
  colors: {
    partA: '#118ab2',
    stock: '#ffd166',
    button: '#118ab2',
    buttonText: '#ffffff',
    text: '#000000'
  }
}

Extras Configuration

Extras are additional services like edge banding, finishing, planing, and machining that can be applied to parts with custom pricing.

Basic Extras Setup

javascript
const initData = {
  stock: [...],

  // Edge banding configuration
  banding: {
    labels: ['type', 'thickness'],
    pricing: {
      'oak|1mm': 1.0,
      'oak|2mm': 1.1,
      'pine|1mm': 2.0,
      'maple|1mm': 3.0
    }
  },

  // Finish configuration
  finish: {
    labels: ['type', 'style'],
    pricing: {
      'spray|matt': 1.0,
      'spray|satin': 1.1,
      'lacquer|matt': 2.0,
      'lacquer|satin': 2.2
    }
  },

  // Planing configuration
  planing: {
    labels: ['type'],
    pricing: {
      'standard': 0.5,
      'premium': 0.8
    }
  },

  options: {...}
}

Extras Location Filtering

Control which sides and faces are available for each extra type by adding a locations property directly to each extra configuration:

javascript
// Available sides: 'side.l1', 'side.l2', 'side.w1', 'side.w2' (main edges)
//                  'side.a', 'side.b', 'side.c', 'side.d' (corners)
// Available faces: 'face.a', 'face.b'

banding: {
  labels: ['material', 'thickness'],
  pricing: {...},
  locations: ['side.l1', 'side.l2', 'side.w1', 'side.w2'] // Only main edges
},

finish: {
  labels: ['type'],
  pricing: {...},
  locations: ['face.a', 'face.b'] // Both faces
},

planing: {
  labels: ['type'],
  pricing: {...},
  locations: ['side.w1', 'side.w2', 'face.a', 'face.b'] // Edges and faces
}

Extras Location Groups

Create custom groupings of locations for bulk operations with optional pricing by adding a groups array directly to each extra configuration:

javascript
planing: {
  labels: ['type'],
  pricing: {
    'standard': 0.5,
    'premium': 0.8
  },
  locations: ['side.w1', 'side.w2', 'face.a', 'face.b'],
  groups: [
    {
      id: 'two-sided',
      label: '2 sided',
      locations: ['face.a', 'face.b'],
      price: 100, // Optional: fixed price for this group
      hideIndividualLocations: true // Optional: hide individual location controls when group is available
    },
    {
      id: 'four-sided',
      label: '4 sided',
      locations: ['face.a', 'face.b', 'side.w1', 'side.w2'],
      price: 200,
      hideIndividualLocations: true
    }
  ]
},

banding: {
  labels: ['material'],
  pricing: {...},
  groups: [
    {
      id: 'all-corners',
      label: 'All Corners',
      locations: ['side.a', 'side.b', 'side.c', 'side.d']
    },
    {
      id: 'long-sides',
      label: 'Long Sides',
      locations: ['side.l1', 'side.l2']
    }
  ]
}

Group Properties:

  • id (required) - Unique identifier for the group
  • label (required) - Display name shown in the UI
  • locations (required) - Array of location strings included in this group
  • price (optional) - Fixed price for the entire group (overrides individual location pricing)
  • hideIndividualLocations (optional) - When true, hides the individual location controls for locations in this group

Extras Validation Rules

Apply dimension constraints to control which parts can have extras applied by adding a rules property directly to each extra configuration:

javascript
planing: {
  labels: ['type'],
  pricing: {...},
  locations: [...],
  rules: {
    t: {
      min: 8,
      max: 230
    },
    shortSide: {
      min: 10,
      max: 620
    },
    longSide: {
      max: 1000
    },
    message: 'Planing requires thickness 8-230mm, short side 10-620mm, and long side max 1000mm'
  }
},

banding: {
  labels: ['material'],
  pricing: {...},
  rules: {
    t: {
      min: 12,
      max: 30
    },
    message: 'Banding is only available for 12-30mm thick materials'
  }
},

finish: {
  labels: ['type'],
  pricing: {...},
  rules: {
    longSide: {
      max: 2400
    },
    shortSide: {
      max: 1200
    },
    message: 'Finish is only available for parts smaller than 2400x1200mm'
  }
}

Validation Rule Fields:

  • longSide.min / longSide.max - Constraints on the longer dimension
  • shortSide.min / shortSide.max - Constraints on the shorter dimension
  • t.min / t.max - Thickness constraints
  • formula (optional) - JavaScript expression for complex validation (can reference longSide, shortSide, t)
  • message - Custom error message shown when validation fails

When a part doesn't meet the validation rules, the extras options will be disabled and an error message displayed.

Complete Extras Example

javascript
const initData = {
  stock: [
    {
      l: 2440,
      w: 1220,
      t: 18,
      cost: 65,
      material: 'Plywood'
    }
  ],

  // Edge banding
  banding: {
    labels: ['material', 'thickness'],
    pricing: {
      'oak|1mm': 1.0,
      'oak|2mm': 1.2,
      'pine|1mm': 0.8,
      'maple|1mm': 1.5
    },
    locations: ['side.l1', 'side.l2', 'side.w1', 'side.w2'],
    rules: {
      t: { min: 12, max: 30 },
      message: 'Banding only available for 12-30mm thick materials'
    }
  },

  // Finishing
  finish: {
    labels: ['type', 'style'],
    pricing: {
      'spray|matt': 5.0,
      'spray|satin': 5.5,
      'lacquer|matt': 8.0,
      'lacquer|satin': 8.5
    },
    locations: ['face.a', 'face.b'],
    rules: {
      longSide: { max: 2400 },
      shortSide: { max: 1200 },
      message: 'Finish is only available for parts smaller than 2400x1200mm'
    }
  },

  // Planing
  planing: {
    labels: ['type'],
    pricing: {
      'standard': 2.0,
      'premium': 3.5
    },
    locations: ['face.a', 'face.b', 'side.w1', 'side.w2'],
    groups: [
      {
        id: 'two-sided',
        label: 'Two Sides (Top & Bottom)',
        locations: ['face.a', 'face.b'],
        price: 100,
        hideIndividualLocations: true
      },
      {
        id: 'four-sided',
        label: 'Four Sides (All Faces & Edges)',
        locations: ['face.a', 'face.b', 'side.w1', 'side.w2'],
        price: 200,
        hideIndividualLocations: true
      }
    ],
    rules: {
      t: { min: 8, max: 230 },
      shortSide: { min: 10, max: 620 },
      longSide: { max: 1000 },
      message: 'Planing requires: thickness 8-230mm, width 10-620mm, length max 1000mm'
    }
  },

  saw: {
    stockType: 'sheet',
    bladeWidth: 3.2,
    cutType: 'guillotine',
    cutPreference: 'flex'
  },

  options: {
    locale: 'en-US',
    currency: 'USD',

    // Enable extras features
    enable: {
      banding: true,
      finish: true,
      planing: true,
      machining: false
    }
  }
}

Machining Configuration

Advanced machining features for holes, corners, and custom operations:

javascript
const initData = {
  machining: {
    faces: {
      enabled: true
    },
    holes: {
      enabled: true,
      defaultDiameter: 5,
      minDiameter: 5,
      maxDiameter: 10,
      enableDepth: true,
      minDepth: 5
    },
    hingeHoles: {
      enabled: true,
      minimumHoleDistance: 10,
      defaultDistanceFromEdge: 22,
      defaultOuterSpacing: 10,
      defaultHingeLength: 50
    },
    shelfHoles: {
      enabled: true,
      diameters: [5, 10]
    },
    corners: {
      enabled: true,
      types: ['radius', 'bevel'],
      enableBanding: true
    }
  },

  options: {
    enable: {
      machining: true
    }
  }
}

Events

The Checkout API dispatches custom window events for integration:

smartcut/ready

Fired when the calculator is loaded and ready to initialize.

javascript
window.addEventListener('smartcut/ready', () => {
  window.smartcutCheckout.init(initData)
})

smartcut/initComplete

Fired when initialization is complete.

javascript
window.addEventListener('smartcut/initComplete', () => {
  console.log('Calculator initialized')
})

smartcut/calculating

Fired when a calculation starts.

javascript
window.addEventListener('smartcut/calculating', () => {
  console.log('Calculation in progress...')
})

smartcut/result

Fired when optimization results are available.

javascript
window.addEventListener('smartcut/result', (e) => {
  const result = e.detail

  // Optimization metadata
  console.log(result.metadata)

  // Parts used in optimization
  console.log(result.parts)

  // Stock sheets used
  console.log(result.stock)

  // Formatted pricing with currency
  console.log(result.checkout.formattedTotalStockCost)
  console.log(result.checkout.formattedBandingCost)
  console.log(result.checkout.formattedFinishCost)
})

smartcut/validationError

Fired when input validation fails.

javascript
window.addEventListener('smartcut/validationError', () => {
  console.log('Please check your inputs')
})

Result

The result object returned via the smartcut/result event contains:

Result Structure

typescript
{
  jobId: string,

  metadata: {
    totalStockCost: number,
    bandingLengthByType: Record<string, number>,
    finishAreaByType: Record<string, number>,
    planingAreaByType: Record<string, number>,
    addedPartTally: Record<string, number>,
    usedStockTally: Record<string, number>,
    unplacedParts: Array<Part>
  },

  parts: Array<{
    l: number,
    w: number,
    t: number,
    q: number,
    material: string,
    name?: string,
    banding?: Record<string, string>,
    finish?: Record<string, string>,
    planing?: Record<string, string>,
    stock: { id: string }
  }>,

  stock: Array<{
    id: string,
    name: string,
    l: number,
    w: number,
    t: number,
    material: string,
    q: number,
    trim?: object,
    cost?: number,
    analysis: object
  }>,

  offcuts: Array<{
    l: number,
    w: number,
    t: number,
    q: number,
    stockId: string
  }>,

  checkout: {
    formattedTotalStockCost: string,
    formattedBandingCost: Record<string, string>,
    formattedFinishCost: Record<string, string>,
    formattedPlaningCost: Record<string, string>
  },

  inputs: {
    parts: Array<Part>
  }
}

Using Result Data

javascript
window.addEventListener('smartcut/result', (e) => {
  const result = e.detail

  // Get total cost
  const totalCost = result.checkout.formattedTotalStockCost

  // Get parts with optimization data
  const optimizedParts = result.parts

  // Get stock usage
  const stockUsed = result.stock

  // Get offcuts for reuse
  const offcuts = result.offcuts

  // Get extras costs breakdown
  const bandingCosts = result.checkout.formattedBandingCost
  const finishCosts = result.checkout.formattedFinishCost
  const planingCosts = result.checkout.formattedPlaningCost
})

Product Filtering

Enable dynamic stock selection with filtering and search:

javascript
const initData = {
  stock: [
    {
      l: 2800,
      w: 2070,
      t: 18,
      cost: 95,
      material: 'MDF',
      name: 'White MDF 2800×2070×18mm',
      category: 'Sheet Materials',
      tags: ['mdf', 'white', 'standard'],
      available: true
    }
  ],

  stockFilter: {
    enabled: true,
    config: {
      displayMode: 'grid', // 'grid' or 'list'
      enableSearch: true,
      itemsPerPage: 20,
      allowMultipleSelection: true,
      availableFilters: [
        {
          field: 'material',
          type: 'multiselect',
          label: 'Material'
        },
        {
          field: 't',
          type: 'range',
          label: 'Thickness (mm)',
          min: 0,
          max: 30
        },
        {
          field: 'category',
          type: 'multiselect',
          label: 'Category'
        }
      ]
    }
  },

  options: {...}
}

Support