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)
Fixed IDs used throughout (adjust to your env)
| ID | Value | Purpose |
|---|---|---|
| @ClientId | 1 | Tenant ID — change to your client |
| @DbName | 'GB5_DEMO' | DatabaseName multi-tenant field |
| @UserId | 1 | CreatedBy / ApprovedById |
| WiProcessId | 10 | WiProcess PK |
| WiSubProcessId | 20 | WiSubProcess PK |
| WiId | 1001 | Work Instruction PK |
| WiSectionId 1 | 101 | Section "Preparation" |
| WiSectionId 2 | 102 | Section "Assembly" |
| WiStepId 1–5 | 201–205 | One per layout type |
| ContainerId 1–6 | 301–306 | Step containers |
| CMS ContentId | 5001 | CMS SOP document |
| CMS ContainerId 1–5 | 601–605 | CMS 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);
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 tests | URL |
|---|---|
| Full WI with all 5 steps | GET {base}/wi/WI/GetWIFullDetail?WiId=1001 |
| Step 1 — Single layout | GET {base}/wi/WI/GetWIFullDetail?WiId=1001 → containers[0] on step 201 |
| Step 2 — Banner layout | containers[0] on step 202 → containerType=2 |
| Step 3 — TwoColumn layout | containers[0] on step 203 → containerType=1 |
| Step 4 — Tab layout | containers[0] on step 204 → 3 unique zone names = 3 tabs |
| Step 5 — Accordion layout | containers[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 tests | URL |
|---|---|
| Full CMS document | GET {base}/cms/Content/GetContentFullDocument?ContentId=5001 |
| Containers for this content | GET {base}/cms/ContentContainer/GetContentContainersByContent?ContentId=5001 |
| Blocks in Single container | GET {base}/cms/ContentBlock/GetContentBlocksByContainer?ContentContainerId=601 |
| Blocks in TwoColumn container | GET {base}/cms/ContentBlock/GetContentBlocksByContainer?ContentContainerId=603 |
| Blocks in Accordion container | GET {base}/cms/ContentBlock/GetContentBlocksByContainer?ContentContainerId=604 |
| Blocks in Tab container | GET {base}/cms/ContentBlock/GetContentBlocksByContainer?ContentContainerId=605 |
| Campaign assignments | GET {base}/cms/Campaign/GetAssignmentList?CampaignId=903 |
| Collection resolve (dynamic list) | GET {base}/cms/Collection/ResolveCollection?CollectionId=902 |
Expected renderer output per step
| Step / Container | Expected visual |
|---|---|
| Step 1 · Single | Paragraph text above; 5-item interactive checklist below — full width, stacked |
| Step 2 · Banner | Full-width background image with "DANGER" title overlaid at bottom; Warning box beneath it |
| Step 3 · TwoColumn | Left column: diagram image + caption; Right column: numbered steps + caution box |
| Step 4 · Tab | 3-tab bar: "Model A (2023+)" / "Model B (2021-2022)" / "Legacy (pre-2021)". Each tab shows different content. |
| Step 5 · Accordion | 3 collapsed panels: "Fastener Specifications" / "Completion Checklist" / "Safety Data Sheet"; QR code below |
21 · Field Notes & Gotchas
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 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.'Model A (2023+)' and 'model a (2023+)' produce two separate tabs. Keep zone names identical across all blocks that share a zone.N'...' Unicode strings. HTML angle brackets inside JSON values are escaped as < and > only when inside HTML strings ("html" field). The outer JSON quotes are standard " characters.'left' and position 1 in zone 'right' are independent. Each zone sorts its own blocks from position 1 upward.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.