WI ModuleCMS Module SQL ServerREST API Tests

Test Data — All 5 Layout Types

Complete INSERT scripts for WI and CMS tables, covering every container type (Single, TwoColumn, Banner, Accordion, Tab) with realistic block content. Run these against your dev database, then call the WI/CMS API to see each layout rendered live.

1 · Data Story & ID Map

All inserts follow a single scenario: "Connector Bracket Installation" — a WI with 5 steps each demonstrating one container layout. The same content is mirrored as a CMS SOP document so you can test both renderers.

WI steps created (one per layout type)

1
Tool Pre-check SingleSection 1 · Checklist block in default single-column layout
2
Safety Warning BannerSection 1 · Full-width danger banner + warning block below
3
Bracket Alignment TwoColumnSection 2 · Image (left zone) + RichText steps (right zone)
4
Torque Application TabSection 2 · Model A / Model B / Legacy — tab per variant
5
Completion & Sign-off AccordionSection 2 · Specs table, completion checklist, download SDS — 3 panels

Fixed IDs used throughout (adjust to your env)

IDValuePurpose
@ClientId1Tenant ID — change to your client
@DbName'GB5_DEMO'DatabaseName multi-tenant field
@UserId1CreatedBy / ApprovedById
WiProcessId10WiProcess PK
WiSubProcessId20WiSubProcess PK
WiId1001Work Instruction PK
WiSectionId 1101Section "Preparation"
WiSectionId 2102Section "Assembly"
WiStepId 1–5201–205One per layout type
ContainerId 1–6301–306Step containers
CMS ContentId5001CMS SOP document
CMS ContainerId 1–5601–605CMS document containers

2 · Variable Block — Paste at Top of Every Script

DECLARE @ClientId   INT          = 1;
DECLARE @DbName     NVARCHAR(100) = N'GB5_DEMO';
DECLARE @UserId     INT          = 1;
DECLARE @Now        DATETIME     = GETUTCDATE();

-- WI IDs
DECLARE @WiProcessId    INT = 10;
DECLARE @WiSubProcessId INT = 20;
DECLARE @WiId           INT = 1001;
DECLARE @SecId1         INT = 101;
DECLARE @SecId2         INT = 102;
DECLARE @StepId1        INT = 201;  -- Single
DECLARE @StepId2        INT = 202;  -- Banner
DECLARE @StepId3        INT = 203;  -- TwoColumn
DECLARE @StepId4        INT = 204;  -- Tab
DECLARE @StepId5        INT = 205;  -- Accordion

-- WiStepContainer IDs
DECLARE @Con301 INT = 301; -- Step1: Single
DECLARE @Con302 INT = 302; -- Step2: Banner
DECLARE @Con303 INT = 303; -- Step3: TwoColumn
DECLARE @Con304 INT = 304; -- Step4: Tab
DECLARE @Con305 INT = 305; -- Step5: Accordion
DECLARE @Con306 INT = 306; -- Step5: extra Single below accordion

-- CMS IDs
DECLARE @SpaceId    INT = 801;
DECLARE @CmsConId   INT = 5001;
DECLARE @CmsCon601  INT = 601; -- CMS Single
DECLARE @CmsCon602  INT = 602; -- CMS Banner
DECLARE @CmsCon603  INT = 603; -- CMS TwoColumn
DECLARE @CmsCon604  INT = 604; -- CMS Accordion
DECLARE @CmsCon605  INT = 605; -- CMS Tab
DECLARE @ChannelId  INT = 901;
DECLARE @CollId     INT = 902;
DECLARE @CampId     INT = 903;
DECLARE @LPathId    INT = 904;

3 · WiProcess WI

INSERT INTO WiProcess
       (WiProcessId, WiProcessCode, WiProcessName, DepartmentId, DisplayOrder,
        Description, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@WiProcessId, N'PROC-ASSY', N'Assembly Operations', 1, 1,
        N'All assembly-line work instructions', 1, @ClientId, @DbName, @UserId, @Now);

4 · WiSubProcess WI

INSERT INTO WiSubProcess
       (WiSubProcessId, WiProcessId, WiSubProcessCode, WiSubProcessName, DisplayOrder,
        Description, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@WiSubProcessId, @WiProcessId, N'SP-BRACKET', N'Bracket Assembly', 1,
        N'Bracket sub-assembly operations', 1, @ClientId, @DbName, @UserId, @Now);

5 · WiHeader WI

ActivityType 0 = Cycle · WiStatus 3 = Published · DisplayContext 0 = All · StepAdvanceMode 0 = Manual

INSERT INTO WiHeader
       (WiId, WiSubProcessId, WiProcessId, WiCode, WiTitle,
        ActivityType, WiVersion, WiStatus, DisplayContext, StepAdvanceMode,
        StdCycleTimeMins, EffectiveDate, ApprovedById, ApprovedOn,
        Description, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@WiId, @WiSubProcessId, @WiProcessId, N'WI-BKT-001', N'Connector Bracket Installation',
        0, N'1.0', 3, 0, 0,
        12, N'2025-01-01', @UserId, @Now,
        N'Install the connector bracket assembly onto the main chassis frame.',
        1, @ClientId, @DbName, @UserId, @Now);

6 · WiSection WI

INSERT INTO WiSection
       (WiSectionId, WiId, SlNo, SectionTitle, Description,
        ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@SecId1, @WiId, 1, N'Preparation & Safety',
        N'Tool verification and safety checks before work begins.',
        @ClientId, @DbName, @UserId, @Now),

       (@SecId2, @WiId, 2, N'Installation',
        N'Bracket alignment, torque application, and completion sign-off.',
        @ClientId, @DbName, @UserId, @Now);

7 · WiStep — 5 steps, one per layout type WI

WarningLevel: 0=None · 1=Caution · 2=Warning · 3=Danger

INSERT INTO WiStep
       (WiStepId, WiId, WiSectionId, SlNo, StepTitle,
        WarningLevel, StdDurationSecs, IsOptional, RequireSignOff, AutoAdvanceSecs,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
-- Step 1: Single container — tool pre-check
       (@StepId1, @WiId, @SecId1, 1, N'Tool & PPE Pre-check',
        0, 120, 0, 0, 0, 1, @ClientId, @DbName, @UserId, @Now),

-- Step 2: Banner container — danger warning
       (@StepId2, @WiId, @SecId1, 2, N'Electrical Isolation Confirmation',
        3, 60, 0, 1, 0, 1, @ClientId, @DbName, @UserId, @Now),

-- Step 3: TwoColumn container — diagram + instructions
       (@StepId3, @WiId, @SecId2, 1, N'Bracket Alignment',
        1, 180, 0, 0, 0, 1, @ClientId, @DbName, @UserId, @Now),

-- Step 4: Tab container — model variants
       (@StepId4, @WiId, @SecId2, 2, N'Torque Application',
        1, 240, 0, 0, 0, 1, @ClientId, @DbName, @UserId, @Now),

-- Step 5: Accordion container — reference + completion
       (@StepId5, @WiId, @SecId2, 3, N'Completion & Sign-off',
        0, 90, 0, 1, 0, 1, @ClientId, @DbName, @UserId, @Now);

8 · WiStepContainer WI · NEW TABLE

ContainerType: 0=Single · 1=TwoColumn · 2=Banner · 3=Accordion · 4=Tab

INSERT INTO WiStepContainer
       (ContainerId, WiStepId, ContainerType, ContainerLabel, PositionNo,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
-- Step 1 → Single (ContainerType=0)
       (@Con301, @StepId1, 0, N'Tool Check',            1, 1, @ClientId, @DbName, @UserId, @Now),

-- Step 2 → Banner (ContainerType=2), then Single below it
       (@Con302, @StepId2, 2, N'Danger Notice',         1, 1, @ClientId, @DbName, @UserId, @Now),

-- Step 3 → TwoColumn (ContainerType=1)
       (@Con303, @StepId3, 1, N'Diagram and Steps',     1, 1, @ClientId, @DbName, @UserId, @Now),

-- Step 4 → Tab (ContainerType=4)
       (@Con304, @StepId4, 4, N'Model Variant Torque', 1, 1, @ClientId, @DbName, @UserId, @Now),

-- Step 5 → Accordion (ContainerType=3) + Single below
       (@Con305, @StepId5, 3, N'Reference & Checklist', 1, 1, @ClientId, @DbName, @UserId, @Now),
       (@Con306, @StepId5, 0, N'Final Action',          2, 1, @ClientId, @DbName, @UserId, @Now);

9 · WiStepContent — all blocks with zone assignments WI

ContentType: 0=RichText 1=Image 2=Video 3=PDF 4=Checklist 5=Warning 6=DecisionBranch 7=File 8=QRCode 9=DataTable 10=Banner
ContentSource: 0=Inline 1=CMS 2=FileStore 3=ExternalURL
Key: ContentRef holds the JSON payload (not contentData — verify field name against your QB file)

Step 1 — Single container: Checklist block

INSERT INTO WiStepContent
       (WiStepContentId, WiStepId, SlNo, ContentType, ContentSource,
        ContentRef, MediaCaption, DisplayDurationSecs, LoopMedia,
        IsActive, ContainerId, ZoneName, BlockInContainerPos,
        ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
-- Block 1: intro richtext
       (3001, @StepId1, 1, 0, 0,
        N'{"html":"<p>Verify all required tools and PPE are present before starting.</p>","plainText":"Verify all required tools and PPE are present before starting."}',
        NULL, 0, 0,
        1, @Con301, N'main', 1,
        @ClientId, @DbName, @UserId, @Now),

-- Block 2: checklist (ContentType=4)
       (3002, @StepId1, 2, 4, 0,
        N'{"title":"Pre-start Checklist","items":[{"id":"c1","label":"Torque wrench (range 10-50 Nm)","checked":false,"isOptional":false},{"id":"c2","label":"Safety glasses","checked":false,"isOptional":false},{"id":"c3","label":"Anti-static wrist strap","checked":false,"isOptional":false},{"id":"c4","label":"Alignment jig (BKT-JIG-01)","checked":false,"isOptional":false},{"id":"c5","label":"M6 hex bolts x6 (pre-torqued bag)","checked":false,"isOptional":false}],"allowPartial":false}',
        NULL, 0, 0,
        1, @Con301, N'main', 2,
        @ClientId, @DbName, @UserId, @Now);

Step 2 — Banner container: danger banner block

INSERT INTO WiStepContent
       (WiStepContentId, WiStepId, SlNo, ContentType, ContentSource,
        ContentRef, MediaCaption, DisplayDurationSecs, LoopMedia,
        IsActive, ContainerId, ZoneName, BlockInContainerPos,
        ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
-- Block 1: Banner block (ContentType=10) — goes inside Banner container
       (3010, @StepId2, 1, 10, 0,
        N'{"title":"DANGER","message":"Ensure main power isolator is in OFF position and locked before proceeding. Tag-out/Lock-out (LOTO) must be applied.","imageUrl":"/assets/wi/banners/electrical-danger.jpg","ctaLabel":"View LOTO Procedure","ctaUrl":"/wi/1002"}',
        NULL, 0, 0,
        1, @Con302, N'main', 1,
        @ClientId, @DbName, @UserId, @Now),

-- Block 2: Warning box below banner (ContentType=5) — Warning stays inline with text
       (3011, @StepId2, 2, 5, 0,
        N'{"message":"Confirm isolation with approved voltage tester. Do not rely on visual switch position alone.","warningLevel":3}',
        NULL, 0, 0,
        1, @Con302, N'main', 2,
        @ClientId, @DbName, @UserId, @Now);
⚠️
The Banner container (ContainerType=2) applies full-width CSS. Its first block should be ContentType=10 (Banner block). Additional blocks in the same 'main' zone stack below the banner image area.

Step 3 — TwoColumn container: image left, steps right

INSERT INTO WiStepContent
       (WiStepContentId, WiStepId, SlNo, ContentType, ContentSource,
        ContentRef, MediaCaption, DisplayDurationSecs, LoopMedia,
        IsActive, ContainerId, ZoneName, BlockInContainerPos,
        ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
-- LEFT zone: diagram image (ContentType=1)
       (3020, @StepId3, 1, 1, 2,
        N'{"imageUrl":"/assets/wi/images/bracket-alignment.png","caption":"Fig 1 — Alignment mark positions","altText":"Bracket with four alignment marks visible on chassis frame"}',
        N'Alignment mark positions', 0, 0,
        1, @Con303, N'left', 1,
        @ClientId, @DbName, @UserId, @Now),

-- RIGHT zone: assembly instructions richtext (ContentType=0)
       (3021, @StepId3, 2, 0, 0,
        N'{"html":"<ol><li>Position bracket so all 4 alignment marks are visible.</li><li>Insert M6 bolts finger-tight into all 6 holes.</li><li>Verify bracket is flush with the datum face (±0.5 mm).</li><li>Confirm no cable routing is pinched behind bracket.</li></ol>","plainText":"1. Position bracket... 2. Insert bolts..."}',
        NULL, 0, 0,
        1, @Con303, N'right', 1,
        @ClientId, @DbName, @UserId, @Now),

-- RIGHT zone: warning below the richtext (ContentType=5) — second block in right zone
       (3022, @StepId3, 3, 5, 0,
        N'{"message":"Brackets must be flush. A gap >0.5 mm requires shimming — see Engineering Note EN-BKT-03.","warningLevel":1}',
        NULL, 0, 0,
        1, @Con303, N'right', 2,
        @ClientId, @DbName, @UserId, @Now);

Step 4 — Tab container: Model A / Model B / Legacy

INSERT INTO WiStepContent
       (WiStepContentId, WiStepId, SlNo, ContentType, ContentSource,
        ContentRef, MediaCaption, DisplayDurationSecs, LoopMedia,
        IsActive, ContainerId, ZoneName, BlockInContainerPos,
        ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
-- TAB: "Model A (2023+)" — richtext + DataTable in same tab
       (3030, @StepId4, 1, 0, 0,
        N'{"html":"<p>Model A uses the revised torque sequence. Apply bolts in the cross-pattern shown on the jig label.</p>","plainText":"Model A uses the revised torque sequence."}',
        NULL, 0, 0,
        1, @Con304, N'Model A (2023+)', 1,
        @ClientId, @DbName, @UserId, @Now),

       (3031, @StepId4, 2, 9, 0,
        N'{"headers":["Fastener","Torque (Nm)","Pass","Tool"],"rows":[["M6 outer x4","10","1st pass","T-10"],["M6 outer x4","18","Final","T-10"],["M6 centre x2","22","Single pass","T-20"]]}',
        NULL, 0, 0,
        1, @Con304, N'Model A (2023+)', 2,
        @ClientId, @DbName, @UserId, @Now),

-- TAB: "Model B (2021-2022)"
       (3032, @StepId4, 3, 0, 0,
        N'{"html":"<p>Model B uses an older bracket profile. Torque in sequential order (1→6) not cross-pattern.</p>","plainText":"Model B uses an older bracket profile."}',
        NULL, 0, 0,
        1, @Con304, N'Model B (2021-2022)', 1,
        @ClientId, @DbName, @UserId, @Now),

       (3033, @StepId4, 4, 9, 0,
        N'{"headers":["Fastener","Torque (Nm)","Tool"],"rows":[["M6 x6","15","T-10"],["M8 centre","30","T-20"]]}',
        NULL, 0, 0,
        1, @Con304, N'Model B (2021-2022)', 2,
        @ClientId, @DbName, @UserId, @Now),

-- TAB: "Legacy (pre-2021)" — with a danger warning block
       (3034, @StepId4, 5, 5, 0,
        N'{"message":"Legacy units require Engineering approval before torque values are applied. Contact ENG team.","warningLevel":2}',
        NULL, 0, 0,
        1, @Con304, N'Legacy (pre-2021)', 1,
        @ClientId, @DbName, @UserId, @Now),

       (3035, @StepId4, 6, 0, 0,
        N'{"html":"<p>Use the legacy torque spec sheet pinned at workstation WS-03-B. Torque to 12 Nm all bolts, single pass.</p>","plainText":"Use the legacy torque spec sheet."}',
        NULL, 0, 0,
        1, @Con304, N'Legacy (pre-2021)', 2,
        @ClientId, @DbName, @UserId, @Now);

Step 5 — Accordion container: 3 panels + a sign-off below

INSERT INTO WiStepContent
       (WiStepContentId, WiStepId, SlNo, ContentType, ContentSource,
        ContentRef, MediaCaption, DisplayDurationSecs, LoopMedia,
        IsActive, ContainerId, ZoneName, BlockInContainerPos,
        ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
-- ACCORDION panel "Fastener Specifications" → DataTable (ContentType=9)
       (3040, @StepId5, 1, 9, 0,
        N'{"headers":["Part No","Description","Qty","Torque Nm","Class"],"rows":[["M6-HEX-SS","M6 Hex Bolt Stainless","6","18","Grade 8"],["M6-WSH-SS","M6 Flat Washer","6","N/A","316 SS"],["LOCTITE-243","Thread Lock Medium","1 drop","N/A","Blue"]]}',
        NULL, 0, 0,
        1, @Con305, N'Fastener Specifications', 1,
        @ClientId, @DbName, @UserId, @Now),

-- ACCORDION panel "Completion Checklist" → Checklist (ContentType=4)
       (3041, @StepId5, 2, 4, 0,
        N'{"title":"Final Inspection Checklist","items":[{"id":"f1","label":"All 6 bolts torqued to spec","checked":false,"isOptional":false},{"id":"f2","label":"Bracket flush with datum (checked with gauge)","checked":false,"isOptional":false},{"id":"f3","label":"Cable routing clear of bracket edges","checked":false,"isOptional":false},{"id":"f4","label":"Loctite 243 applied and cured (min 10 min)","checked":false,"isOptional":false},{"id":"f5","label":"Work area cleaned of metal swarf","checked":false,"isOptional":false}],"allowPartial":false}',
        NULL, 0, 0,
        1, @Con305, N'Completion Checklist', 1,
        @ClientId, @DbName, @UserId, @Now),

-- ACCORDION panel "Safety Data Sheet" → File download (ContentType=7)
       (3042, @StepId5, 3, 7, 2,
        N'{"fileUrl":"/files/sds/loctite-243-sds-en.pdf","fileName":"Loctite 243 Safety Data Sheet.pdf","fileSize":204800}',
        NULL, 0, 0,
        1, @Con305, N'Safety Data Sheet', 1,
        @ClientId, @DbName, @UserId, @Now),

-- Second container (Con306, Single) — QR code for digital sign-off
       (3043, @StepId5, 4, 8, 0,
        N'{"targetUrl":"/wi/signoff?wiId=1001&stepId=205","label":"Scan to record digital sign-off"}',
        NULL, 0, 0,
        1, @Con306, N'main', 1,
        @ClientId, @DbName, @UserId, @Now);

10 · ContentContainerType — seed master CMS

INSERT INTO ContentContainerType
       (ContentContainerTypeId, ContainerTypeCode, ContainerTypeName, ContainerTypeValue,
        DisplayOrder, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (1, N'SINGLE',    N'Single Column',  0, 1, 1, @ClientId, @DbName, @UserId, @Now),
       (2, N'TWOCOL',    N'Two Column',     1, 2, 1, @ClientId, @DbName, @UserId, @Now),
       (3, N'BANNER',    N'Banner',         2, 3, 1, @ClientId, @DbName, @UserId, @Now),
       (4, N'ACCORDION', N'Accordion',      3, 4, 1, @ClientId, @DbName, @UserId, @Now),
       (5, N'TAB',       N'Tab Panel',      4, 5, 1, @ClientId, @DbName, @UserId, @Now);

11 · Space CMS / DMS

INSERT INTO Space
       (SpaceId, SpaceCode, SpaceName, Description,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@SpaceId, N'MFGSOP', N'Manufacturing SOPs',
        N'Standard Operating Procedures for the assembly floor.',
        1, @ClientId, @DbName, @UserId, @Now);

12 · Content CMS

ContentStatus: 0=Draft · 1=InReview · 2=Approved · 3=Published · 4=Archived

INSERT INTO Content
       (ContentId, SpaceId, ContentTypeId, ContentTitle, ContentCode,
        ContentStatus, AuthorId, Description,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@CmsConId, @SpaceId, 1, N'Connector Bracket Installation — SOP', N'SOP-BKT-001',
        3, @UserId, N'Step-by-step SOP for installing the connector bracket assembly.',
        1, @ClientId, @DbName, @UserId, @Now);

13 · ContentContainer — 5 containers covering all layout types CMS

INSERT INTO ContentContainer
       (ContentContainerId, ContentId, ContentContainerTypeId, ContainerTypeCode,
        ContainerLabel, PositionNo,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@CmsCon601, @CmsConId, 1, N'SINGLE',    N'Introduction',     1, 1, @ClientId, @DbName, @UserId, @Now),
       (@CmsCon602, @CmsConId, 3, N'BANNER',    N'Safety Notice',    2, 1, @ClientId, @DbName, @UserId, @Now),
       (@CmsCon603, @CmsConId, 2, N'TWOCOL',    N'Process Overview', 3, 1, @ClientId, @DbName, @UserId, @Now),
       (@CmsCon604, @CmsConId, 4, N'ACCORDION', N'Reference Data',  4, 1, @ClientId, @DbName, @UserId, @Now),
       (@CmsCon605, @CmsConId, 5, N'TAB',       N'Model Variants',  5, 1, @ClientId, @DbName, @UserId, @Now);

14 · ContentBlock — blocks per container CMS

PropsJson is the equivalent of WI's ContentRef — the JSON payload the renderer reads.

Container 601: Single — intro richtext

INSERT INTO ContentBlock
       (ContentBlockId, ContentContainerId, BlockCode, ZoneName, PositionNo,
        PropsJson, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (4001, @CmsCon601, N'RICHTEXT', N'main', 1,
        N'{"html":"<h2>Purpose</h2><p>This SOP defines the standard method for installing the connector bracket assembly onto the main chassis frame. It applies to all Model A and Model B production units.</p><h3>Scope</h3><p>All assembly operators on Line 3 and Line 4 are required to follow this procedure.</p>","plainText":"Purpose - This SOP defines..."}',
        1, @ClientId, @DbName, @UserId, @Now),

       (4002, @CmsCon601, N'TABLE', N'main', 2,
        N'{"headers":["Field","Value"],"rows":[["Document No","SOP-BKT-001"],["Revision","1.0"],["Effective Date","2025-01-01"],["Owner","Manufacturing Engineering"],["Review Due","2026-01-01"]]}',
        1, @ClientId, @DbName, @UserId, @Now);

Container 602: Banner — safety notice

INSERT INTO ContentBlock
       (ContentBlockId, ContentContainerId, BlockCode, ZoneName, PositionNo,
        PropsJson, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (4010, @CmsCon602, N'BANNER', N'main', 1,
        N'{"title":"MANDATORY SAFETY REQUIREMENT","message":"LOTO (Lock-out / Tag-out) must be applied before any bracket installation work. Failure to comply is a dismissal offence.","imageUrl":"/assets/cms/banners/loto-mandatory.jpg"}',
        1, @ClientId, @DbName, @UserId, @Now);

Container 603: TwoColumn — overview left, requirements table right

INSERT INTO ContentBlock
       (ContentBlockId, ContentContainerId, BlockCode, ZoneName, PositionNo,
        PropsJson, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (4020, @CmsCon603, N'RICHTEXT', N'left', 1,
        N'{"html":"<h3>Process Summary</h3><ol><li>Apply LOTO and verify isolation</li><li>Verify tools and PPE</li><li>Align bracket to datum marks</li><li>Apply fasteners finger-tight</li><li>Torque to specification</li><li>Inspect and sign-off</li></ol>","plainText":"Process Summary..."}',
        1, @ClientId, @DbName, @UserId, @Now),

       (4021, @CmsCon603, N'TABLE', N'right', 1,
        N'{"headers":["Requirement","Detail"],"rows":[["Competency","Bracket Assy Level 2"],["PPE","Safety glasses, anti-static strap"],["Tools","T-10, T-20 torque wrench"],["Materials","M6 bolts x6, M6 washers x6, Loctite 243"],["Estimated Time","12 minutes"]]}',
        1, @ClientId, @DbName, @UserId, @Now);

Container 604: Accordion — 3 reference panels

INSERT INTO ContentBlock
       (ContentBlockId, ContentContainerId, BlockCode, ZoneName, PositionNo,
        PropsJson, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
-- Panel 1: Torque Values table
       (4030, @CmsCon604, N'TABLE', N'Torque Specifications', 1,
        N'{"headers":["Fastener","Torque Nm","Pass","Note"],"rows":[["M6 outer x4","10","1st pass","Cross-pattern"],["M6 outer x4","18","Final","Cross-pattern"],["M6 centre x2","22","Single pass","Sequential"]]}',
        1, @ClientId, @DbName, @UserId, @Now),

-- Panel 2: Checklist for QC acceptance
       (4031, @CmsCon604, N'CHECKLIST', N'QC Acceptance Criteria', 1,
        N'{"title":"Inspection Points","items":[{"id":"q1","label":"All bolts at final torque (18 / 22 Nm)","checked":false},{"id":"q2","label":"Zero gap between bracket and datum face","checked":false},{"id":"q3","label":"Loctite visible and not cured (applied within last 10 min)","checked":false},{"id":"q4","label":"No burrs or surface damage on bracket","checked":false}],"allowPartial":false}',
        1, @ClientId, @DbName, @UserId, @Now),

-- Panel 3: Related documents download
       (4032, @CmsCon604, N'FILE', N'Related Documents', 1,
        N'{"fileUrl":"/files/eng/EN-BKT-03-shimming.pdf","fileName":"Engineering Note EN-BKT-03 — Shimming Procedure.pdf","fileSize":153600}',
        1, @ClientId, @DbName, @UserId, @Now),

       (4033, @CmsCon604, N'FILE', N'Related Documents', 2,
        N'{"fileUrl":"/files/sds/loctite-243-sds-en.pdf","fileName":"Loctite 243 Safety Data Sheet.pdf","fileSize":204800}',
        1, @ClientId, @DbName, @UserId, @Now);

Container 605: Tab — model variant instructions

INSERT INTO ContentBlock
       (ContentBlockId, ContentContainerId, BlockCode, ZoneName, PositionNo,
        PropsJson, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (4040, @CmsCon605, N'RICHTEXT', N'Model A (2023+)', 1,
        N'{"html":"<p>Apply bolts in cross-pattern. Refer to jig label BKT-JIG-01A.</p><ul><li>1st pass: 10 Nm all bolts</li><li>Final: 18 Nm outer, 22 Nm centre</li></ul>","plainText":"Apply bolts in cross-pattern."}',
        1, @ClientId, @DbName, @UserId, @Now),

       (4041, @CmsCon605, N'RICHTEXT', N'Model B (2021-2022)', 1,
        N'{"html":"<p>Apply bolts in sequential order 1→6. Single-pass torque: 15 Nm all bolts, centre bolt 30 Nm.</p>","plainText":"Apply bolts in sequential order."}',
        1, @ClientId, @DbName, @UserId, @Now),

       (4042, @CmsCon605, N'WARNING', N'Legacy (pre-2021)', 1,
        N'{"message":"Legacy units require Engineering approval. Contact ENG team before proceeding.","warningLevel":2}',
        1, @ClientId, @DbName, @UserId, @Now),

       (4043, @CmsCon605, N'RICHTEXT', N'Legacy (pre-2021)', 2,
        N'{"html":"<p>Use legacy spec sheet at WS-03-B. Torque: 12 Nm all bolts, single pass.</p>","plainText":"Use legacy spec sheet."}',
        1, @ClientId, @DbName, @UserId, @Now);

15 · Channel + ChannelContent CMS

INSERT INTO Channel
       (ChannelId, ChannelCode, ChannelName, Description,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@ChannelId, N'FLOOR-ASSY', N'Assembly Floor Feed',
        N'Content broadcast to assembly floor displays.',
        1, @ClientId, @DbName, @UserId, @Now);

INSERT INTO ChannelContent
       (ChannelId, ContentId, DisplayOrder, IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@ChannelId, @CmsConId, 1, 1, @ClientId, @DbName, @UserId, @Now);

16 · Collection CMS

INSERT INTO Collection
       (CollectionId, CollectionCode, CollectionName, Description,
        FilterSpaceId, FilterContentTypeId, FilterContentStatus,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@CollId, N'COLL-MFG-SOPS', N'All Manufacturing SOPs',
        N'Dynamic collection — all Published SOPs in the Manufacturing space.',
        @SpaceId, 1, 3,
        1, @ClientId, @DbName, @UserId, @Now);

17 · Campaign + CampaignAssignment CMS

CampaignType: 0=Notification · 1=Assignment · 2=LearningPath
CampaignStatus: 0=Draft · 1=Sent · 2=Closed

INSERT INTO Campaign
       (CampaignId, CampaignCode, CampaignName, CampaignType, CampaignStatus,
        Description, ContentId, TargetAudienceJson,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@CampId, N'CAMP-BKT-001', N'Bracket SOP — Mandatory Read', 1, 0,
        N'Mandatory SOP reading assignment for all Line 3 and Line 4 operators.',
        @CmsConId,
        N'{"lines":["Line 3","Line 4"],"roles":["Operator","Inspector"]}',
        1, @ClientId, @DbName, @UserId, @Now);

-- Sample assignment rows (one per operator; replace EmployeeId values)
INSERT INTO CampaignAssignment
       (CampaignId, ContentId, EmployeeId, AssignedOn, CompletedOn, AssignmentStatus,
        ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@CampId, @CmsConId, 1001, @Now, NULL, 0, @ClientId, @DbName, @UserId, @Now),
       (@CampId, @CmsConId, 1002, @Now, NULL, 0, @ClientId, @DbName, @UserId, @Now),
       (@CampId, @CmsConId, 1003, @Now, NULL, 0, @ClientId, @DbName, @UserId, @Now);

18 · LearningPath + LearningPathItem CMS

INSERT INTO LearningPath
       (LearningPathId, PathCode, PathName, Description,
        IsActive, ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@LPathId, N'LP-ASSY-L1', N'Assembly Operator Level 1',
        N'Mandatory onboarding path for new assembly operators. Complete all items in order.',
        1, @ClientId, @DbName, @UserId, @Now);

INSERT INTO LearningPathItem
       (LearningPathId, ContentId, PositionNo, IsRequired,
        ClientId, DatabaseName, CreatedBy, CreatedDate)
VALUES
       (@LPathId, 4999, 1, 1, @ClientId, @DbName, @UserId, @Now),  -- Safety Induction (pre-existing)
       (@LPathId, @CmsConId, 2, 1, @ClientId, @DbName, @UserId, @Now), -- Bracket SOP (this doc)
       (@LPathId, 5002, 3, 0, @ClientId, @DbName, @UserId, @Now);  -- Torque Theory (optional)

19 · Verify SELECTs — run after inserts

-- Check WI full structure
SELECT s.SlNo, s.SectionTitle,
       p.SlNo AS StepNo, p.StepTitle,
       c.ContainerId, c.ContainerType, c.ContainerLabel, c.PositionNo,
       b.WiStepContentId, b.ContentType, b.ZoneName, b.BlockInContainerPos
FROM   WiSection s
JOIN   WiStep p          ON p.WiId = s.WiId AND p.WiSectionId = s.WiSectionId
JOIN   WiStepContainer c ON c.WiStepId = p.WiStepId
JOIN   WiStepContent b   ON b.ContainerId = c.ContainerId
WHERE  s.WiId = @WiId
ORDER BY s.SlNo, p.SlNo, c.PositionNo, b.ZoneName, b.BlockInContainerPos;

-- Check CMS document containers and blocks
SELECT c.PositionNo, c.ContainerTypeCode, c.ContainerLabel,
       b.ContentBlockId, b.BlockCode, b.ZoneName, b.PositionNo AS BlockPos
FROM   ContentContainer c
JOIN   ContentBlock b ON b.ContentContainerId = c.ContentContainerId
WHERE  c.ContentId = @CmsConId
ORDER BY c.PositionNo, b.ZoneName, b.PositionNo;

-- Block count per container (should be: 2, 2, 3, 4, 4 for steps 1-5)
SELECT c.ContainerId, c.ContainerLabel,
       c.ContainerType,
       COUNT(b.WiStepContentId) AS BlockCount
FROM   WiStepContainer c
LEFT JOIN WiStepContent b ON b.ContainerId = c.ContainerId
WHERE  c.ContainerId BETWEEN @Con301 AND @Con306
GROUP BY c.ContainerId, c.ContainerLabel, c.ContainerType
ORDER BY c.ContainerId;

20 · API Test Calls — render each layout type

After inserting, call these endpoints. The Angular renderer will display each layout type. Use your host + port for {base}.

WI endpoints

What it testsURL
Full WI with all 5 stepsGET {base}/wi/WI/GetWIFullDetail?WiId=1001
Step 1 — Single layoutGET {base}/wi/WI/GetWIFullDetail?WiId=1001 → containers[0] on step 201
Step 2 — Banner layoutcontainers[0] on step 202 → containerType=2
Step 3 — TwoColumn layoutcontainers[0] on step 203 → containerType=1
Step 4 — Tab layoutcontainers[0] on step 204 → 3 unique zone names = 3 tabs
Step 5 — Accordion layoutcontainers[0] on step 205 → 3 unique zone names = 3 panels
Print view (all sections)Use <wi-print [wiId]="1001" /> component directly in a route

CMS endpoints

What it testsURL
Full CMS documentGET {base}/cms/Content/GetContentFullDocument?ContentId=5001
Containers for this contentGET {base}/cms/ContentContainer/GetContentContainersByContent?ContentId=5001
Blocks in Single containerGET {base}/cms/ContentBlock/GetContentBlocksByContainer?ContentContainerId=601
Blocks in TwoColumn containerGET {base}/cms/ContentBlock/GetContentBlocksByContainer?ContentContainerId=603
Blocks in Accordion containerGET {base}/cms/ContentBlock/GetContentBlocksByContainer?ContentContainerId=604
Blocks in Tab containerGET {base}/cms/ContentBlock/GetContentBlocksByContainer?ContentContainerId=605
Campaign assignmentsGET {base}/cms/Campaign/GetAssignmentList?CampaignId=903
Collection resolve (dynamic list)GET {base}/cms/Collection/ResolveCollection?CollectionId=902

Expected renderer output per step

Step / ContainerExpected visual
Step 1 · SingleParagraph text above; 5-item interactive checklist below — full width, stacked
Step 2 · BannerFull-width background image with "DANGER" title overlaid at bottom; Warning box beneath it
Step 3 · TwoColumnLeft column: diagram image + caption; Right column: numbered steps + caution box
Step 4 · Tab3-tab bar: "Model A (2023+)" / "Model B (2021-2022)" / "Legacy (pre-2021)". Each tab shows different content.
Step 5 · Accordion3 collapsed panels: "Fastener Specifications" / "Completion Checklist" / "Safety Data Sheet"; QR code below

21 · Field Notes & Gotchas

🚫
ContentRef vs PropsJson — WiStepContent uses ContentRef as the JSON payload column. CMS ContentBlock uses PropsJson. Both hold the same JSON structure for the same content type. Verify against your backend QB file before running inserts — your actual column name may differ.
⚠️
IDENTITY vs explicit IDs — If your tables use IDENTITY PKs, either use SET IDENTITY_INSERT TableName ON before the inserts and OFF after, or remove the explicit ID columns and capture new IDs with SCOPE_IDENTITY() or OUTPUT INSERTED.Id.
✅
Zone name case is exact-match — The renderer groups blocks by zone name string equality. 'Model A (2023+)' and 'model a (2023+)' produce two separate tabs. Keep zone names identical across all blocks that share a zone.
✅
JSON escaping in T-SQL strings — All JSON in the inserts above uses N'...' Unicode strings. HTML angle brackets inside JSON values are escaped as &lt; and &gt; only when inside HTML strings ("html" field). The outer JSON quotes are standard " characters.
ℹ️
BlockInContainerPos is per-zone — Position 1 in zone 'left' and position 1 in zone 'right' are independent. Each zone sorts its own blocks from position 1 upward.
ℹ️
ContentSource values — 0=Inline (JSON in ContentRef), 1=CMS (ContentRef holds a ContentId to pull from CMS), 2=FileStore (URL in ContentRef), 3=ExternalURL. Most test blocks use 0 (Inline) so no external dependency is needed to test the renderer.