Appearance
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 grouplabel(required) - Display name shown in the UIlocations(required) - Array of location strings included in this groupprice(optional) - Fixed price for the entire group (overrides individual location pricing)hideIndividualLocations(optional) - Whentrue, 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 dimensionshortSide.min/shortSide.max- Constraints on the shorter dimensiont.min/t.max- Thickness constraintsformula(optional) - JavaScript expression for complex validation (can referencelongSide,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
- Email: hello@cutrevolution.com
- Chat: https://smartcut.dev/