1 view_column Container Layout Types (WiContainerType)
Every WI step is composed of one or more containers. Each container has a fixed zone layout. Blocks (content items) are placed into named zones.
Single
= 0
main
containerType: 0
containerTypeCode: SINGLE
zoneName: "main"
Default container type. All blocks go into the single main zone.
TwoColumn
= 1
left
right
containerType: 1
containerTypeCode: TWOCOL
zoneNames: "left""right"
Each zone is independent. Either zone can be empty — renderer collapses gracefully to single-column.
Banner
= 2
banner
containerType: 2
containerTypeCode: BANNER
zoneName: "banner"
Typically contains a single Banner block (ContentType=10). Can also contain RichText or Image.
Accordion
= 3
▼ section-1
▶ section-2
▶ section-3
containerType: 3
containerTypeCode: ACCORDION
zoneName pattern: "section-{N}"
containerLabel on each section becomes the accordion header. + Add Section creates a new zone appended as section-N.
Tab
= 4
tab-1
tab-2
tab-3
containerType: 4
containerTypeCode: TAB
zoneName pattern: "tab-{N}"
containerLabel on each tab becomes the tab title. Active tab index is tracked in the player service — not stored in the content model.
2 widgets Content Block Types (ContentType)
Blocks are the atomic units of content. Each block has a contentType (enum 0–10), a contentSource (enum 0–3), and type-specific data stored in contentRef or additional fields.
text_fields
RichText ContentType = 0
blockCode: RICHTEXT
contentRef format: Raw HTML string (TipTap output)
"<p>Check base metal for <strong>surface contamination</strong> and moisture.</p>"
Compatible Sources
Inline CMS
Sanitized with DOMPurify on render. Never bind raw HTML directly.
image
Image ContentType = 1
blockCode: IMAGE
contentRef format: Image URL string
"https://cdn.example.com/wi/weld-prep-diagram.jpg"
extra fields: mediaCaption · zoomRegion
// zoomRegion — percentage values (0–100)
{ "x": 10, "y": 20, "w": 80, "h": 60 }
Compatible Sources
Inline FileStore ExternalURL CMS
videocam
Video ContentType = 2
blockCode: VIDEO
contentRef format: Video URL
"https://cdn.example.com/wi/arc-welding-technique.mp4"
extra fields: loopMedia: 0|1 · displayDurationSecs
Compatible Sources
Inline FileStore ExternalURL CMS
If displayDurationSecs > 0 AND loopMedia = 0, player auto-advances after video ends.
picture_as_pdf
PDF ContentType = 3
blockCode: PDF
contentRef format: PDF URL
"https://cdn.example.com/wi/safety-datasheet-WLD-001.pdf"
extra fields: mediaCaption
Compatible Sources
Inline FileStore ExternalURL CMS
checklist
Checklist ContentType = 4
blockCode: CHECKLIST
contentRef format: JSON array of WiChecklistItem[]
[
  {
    "id": "c1",
    "label": "Verify PPE is worn correctly",
    "checked": false,
    "isOptional": false
  },
  {
    "id": "c2",
    "label": "Check shielding gas pressure (5–7 PSI)",
    "checked": false,
    "isOptional": false
  },
  {
    "id": "c3",
    "label": "Inspect electrode condition",
    "checked": false,
    "isOptional": true
  }
]
Compatible Sources
Inline CMS
isOptional: true items do not block step sign-off. allDone computation ignores optional unchecked items.
warning
Warning ContentType = 5
blockCode: WARNING
contentRef format: Integer string "0"–"3" (WarningLevel)
WarningLevel values
"0" → None "1" → Caution (yellow) "2" → Warning (orange) "3" → Danger (red)
"2" // → renders Warning (orange) banner
Compatible Sources
Inline
contentRef is parsed with parseInt(ref, 10). Defaults to WarningLevel.Warning (2) if NaN.
call_split
DecisionBranch ContentType = 6
blockCode: DECISION
contentRef format: JSON array of WiDecisionBranch[]
[
  {
    "id": "b1",
    "label": "Proceed — base metal clean",
    "description": "Surface passes visual inspection",
    "targetStepId": 3
  },
  {
    "id": "b2",
    "label": "Clean & Retry",
    "description": "Surface contamination found",
    "targetStepId": 1
  }
]
Compatible Sources
Inline
Emits branchSelected event with targetStepId. WI player calls jumpToStep(id).
attach_file
File ContentType = 7
blockCode: FILE
contentRef format: File download URL
"https://cdn.example.com/wi/electrode-spec-sheet.xlsx"
extra fields: mediaCaption (used as file label)
Compatible Sources
FileStore ExternalURL CMS
qr_code
QRCode ContentType = 8
blockCode: QRCODE
contentRef format: Data string to encode (URL, text, form ID)
"https://forms.goodbooks.app/audit/WLD-001?step=2"
Compatible Sources
Inline ExternalURL
QR rendered client-side from contentRef string. Scan opens the URL in the operator's browser.
table_chart
DataTable ContentType = 9
blockCode: TABLE
contentRef format: JSON object with headers + rows
{
  "headers": [
    "Part No.", "Description", "Qty", "Unit"
  ],
  "rows": [
    ["EL-6013", "Electrode 3.2mm", "5", "pcs"],
    ["SG-AR", "Argon Shielding Gas", "1", "cylinder"],
    ["MK-WH", "Welding Helmet Auto-Dark", "1", "unit"]
  ]
}
Compatible Sources
Inline CMS
view_day
Banner ContentType = 10
blockCode: BANNER
contentRef format: JSON object with title, subtitle, colors
{
  "title": "STEP 2: Arc Welding Execution",
  "subtitle": "Standard cycle time: 4 min 30 sec",
  "bgColor": "#0f172a",
  "bgImageUrl": ""
}
Compatible Sources
Inline CMS
3 source Content Source (ContentSource)
Value Enum Name UI Label (WI) UI Label (CMS) Description
0 Inline Inline Write Content stored directly in DB contentRef field. No external dependency.
1 CMS CMS From Library contentRef holds a CMS contentId. Content loaded at render time via CMS API.
2 FileStore FileStore Upload Binary uploaded to blob storage. contentRef = storage object key.
3 ExternalURL External URL Embed URL URL typed by author. Browser fetches directly at runtime.
Source × Block Type Compatibility Matrix
Block Type Inline CMS FileStore ExternalURL
RichText ✓ ✓ — —
Image ✓ ✓ ✓ ✓
Video ✓ ✓ ✓ ✓
PDF ✓ ✓ ✓ ✓
Checklist ✓ ✓ — —
Warning ✓ — — —
DecisionBranch ✓ — — —
File — ✓ ✓ ✓
QRCode ✓ — — ✓
DataTable ✓ ✓ — —
Banner ✓ ✓ — —
4 data_object WiStepContent Interface Reference
interface WiStepContent {
  wiStepContentId:      number;           // negative = unsaved new item
  wiStepId:             number;
  slNo:                 number;           // sort order within zone (1-based)
  contentType:          ContentType;      // enum 0–10
  contentSource:        ContentSource;    // enum 0–3 (default: 0 = Inline)
  contentRef?:          string;           // URL, HTML, or JSON string — varies by type
  mediaCaption?:        string;           // alt text / label for media blocks
  displayDurationSecs:  number;           // 0 = no auto-advance
  loopMedia:            0 | 1;           // video loop flag
  zoomRegion?:          WiZoomRegion;     // Image only: { x, y, w, h } percentages
  applicableModelIds?:  number[] | null;  // product model filter (null = all models)
  isActive:             0 | 1;
  // Container assignment
  containerId?:         number;
  containerType?:       WiContainerType;
  containerLabel?:      string;
  zoneName?:            string;           // e.g. "left", "tab-1", "section-2"
  blockInContainerPos?: number;           // position within zone
}

interface WiZoomRegion {
  x: number;   // left offset % (0–100)
  y: number;   // top offset %
  w: number;   // width %
  h: number;   // height %
}
Field Type Required Description
wiStepContentId number Required Primary key. Use negative values (e.g. -1, -2) for new unsaved blocks.
wiStepId number Required Foreign key to the parent WiStep.
slNo number Required 1-based sort order within the zone. Controls render sequence.
contentType ContentType Required Enum 0–10. Determines how contentRef is interpreted.
contentSource ContentSource Default: 0 Enum 0–3. Where the content lives (Inline, CMS, FileStore, ExternalURL).
contentRef string Optional Payload varies by contentType. May be HTML, a URL, or a JSON string.
mediaCaption string Optional Alt text for images; label for file downloads. Recommended for accessibility.
displayDurationSecs number Default: 0 0 = no auto-advance. Positive integer triggers timer-based advance.
loopMedia 0 | 1 Default: 0 Video-only. 1 = loop indefinitely. Combine with displayDurationSecs=0 for infinite loop.
zoomRegion WiZoomRegion Optional Image-only. Defines a hot-zone for zoom/pan interaction (percentage values 0–100).
applicableModelIds number[] | null Optional null = applies to all product models. Non-null = visible only to matching model IDs.
isActive 0 | 1 Required Soft-delete flag. Inactive blocks are filtered out by the renderer.
containerId number Optional Links block to a specific container. undefined = legacy flat layout.
containerType WiContainerType Optional Denormalized container type for renderer. Enum 0–4.
containerLabel string Optional Accordion section header or tab title. Used when containerType is 3 or 4.
zoneName string Optional Named zone within container. E.g. "left", "tab-1", "section-2".
blockInContainerPos number Optional 1-based position of this block within its zone. Distinct from global slNo.
5 list_alt WiStep Interface Reference
interface WiStep {
  wiStepId:         number;
  wiId:             number;
  wiSectionId:      number;
  slNo:             number;
  stepDisplayNo?:   string;          // e.g. "1.1", "2.3"
  stepTitle:        string;
  warningLevel:     WarningLevel;    // 0=None, 1=Caution, 2=Warning, 3=Danger
  stdDurationSecs:  number;
  toolsRequired?:   WiTool[];
  isOptional:       0 | 1;
  requireSignOff:   0 | 1;
  autoAdvanceSecs:  number;          // 0 = manual advance
  isActive:         0 | 1;
  contents?:        WiStepContent[]; // flat list — backward compat only
  containers?:     WiStepContainer[];
}

interface WiStepContainer {
  containerId:      number;
  containerType:    WiContainerType;
  containerLabel?:  string;
  blocks:           WiStepContent[];
}
WarningLevel Enum Reference
Value Name CSS Class Icon Use Case
0 None — — No warning needed. Standard informational step.
1 Caution level-caution ⚠ yellow Attention required. Potential for minor injury or quality issue.
2 Warning level-warning ⚠ orange Risk of injury or significant equipment damage if step is skipped.
3 Danger level-danger ⛔ red Immediate risk to life or critical equipment. Must not be bypassed.
6 checklist Content Authoring Checklist
Use before publishing a Work Instruction.

Pre-Publish Verification

Every step has at least one container with at least one block.
TwoColumn containers have at least one block in each zone. Empty zones collapse but author intent should be explicit.
Accordion and Tab containers have at least 2 sections/tabs populated.
Steps with requireSignOff = 1 include a Checklist block so the operator has items to verify.
Steps with warningLevel > 0 include a Warning block matching the step's warningLevel value.
All Image blocks have mediaCaption set for accessibility and screen readers.
slNo values are sequential from 1 within each zone — no gaps or duplicates.
DecisionBranch targetStepId values reference valid step IDs in the same WI. Broken links cause runtime jump failures.
Video blocks with displayDurationSecs = 0 AND loopMedia = 1 are intentional — this creates an infinite loop with no auto-advance.
New blocks created in the designer use negative wiStepContentId (e.g. -1, -2) until the step is saved to the API.
7 skip_next Step Advance Mode (StepAdvanceMode)
Value Name Behavior
0 Manual Operator taps or clicks the NEXT button to advance to the next step. Default mode for most WIs.
1 BarcodeScan Step advance is triggered by a barcode scan event from a connected scanner or camera. Used for assembly verification flows.
2 TimerAuto Auto-advances after stdDurationSecs elapses. Countdown displayed to operator. Suitable for timed processes (curing, cooling).
3 Spacebar Advances on Space keypress. Designed for hands-free workstation kiosks where operator cannot touch a screen.
4 Configurable Behavior is set per-workstation in WorkstationConfig. Same WI can be run in Manual mode on one station and BarcodeScan on another.