11<!DOCTYPE html>
22< html lang ="en ">
33< head >
4- < meta charset ="UTF-8 " />
4+ < meta charset ="UTF-8 "/>
55 < title > Campaign Config Validator - NHS Digital</ title >
66 < script src ="https://cdn.jsdelivr.net/pyodide/v0.26.4/full/pyodide.js "> </ script >
77 < style >
2020 padding : 12px 16px ;
2121 border-bottom : 4px solid white; /* NHS Standard header spacing */
2222 }
23+
2324 .nhs-header-content {
2425 max-width : 960px ;
2526 margin : 0 auto;
2627 display : flex;
2728 align-items : center;
2829 }
30+
2931 .nhs-logo {
3032 width : 80px ;
3133 height : 32px ;
3234 }
35+
3336 .nhs-container {
3437 max-width : 960px ;
3538 margin : 2rem auto;
3841 box-sizing : border-box;
3942 flex : 1 ;
4043 }
44+
4145 h1 {
4246 font-size : 2.5rem ;
4347 font-weight : 700 ;
4448 margin-bottom : 1.5rem ;
4549 }
50+
4651 .nhs-card {
4752 background : white;
4853 border : 1px solid # d8dde0 ;
4954 padding : 2rem ;
5055 margin-bottom : 2rem ;
5156 }
57+
5258 .status-bar {
5359 background : # e8edee ;
5460 color : # 212b32 ;
6066 justify-content : space-between;
6167 align-items : center;
6268 }
69+
6370 .nhs-button {
6471 background-color : # 007f3b ; /* NHS Green */
6572 color : white;
7683 text-decoration : none;
7784 display : inline-block;
7885 }
86+
7987 .nhs-button : hover : not (: disabled ) {
8088 background-color : # 00642e ; /* Darker Green hover */
8189 }
90+
8291 .nhs-button : active : not (: disabled ) {
8392 transform : translateY (4px ); /* Button press effect */
8493 box-shadow : none;
8594 }
95+
8696 .nhs-button : disabled {
8797 background-color : # d8dde0 ;
8898 color : # 768692 ;
8999 box-shadow : none;
90100 cursor : not-allowed;
91101 }
102+
92103 label {
93104 display : block;
94105 margin-bottom : 8px ;
95106 font-weight : 600 ;
96107 font-size : 1.1rem ;
97108 }
109+
98110 input [type = "file" ] {
99111 font-size : 1.1rem ;
100112 padding : 10px ;
104116 box-sizing : border-box;
105117 cursor : pointer;
106118 }
119+
107120 .log {
108121 background : # 212b32 ;
109122 color : white;
116129 border-left : 10px solid # 768692 ;
117130 }
118131
119- .ansi-green { color : # 007f3b ; font-weight : bold; background : # ccffdd ; padding : 2px 5px ; }
120- .ansi-red { color : # d81e05 ; font-weight : bold; background : # ffd1d1 ; padding : 2px 5px ;}
121- .ansi-yellow { color : # ffb81c ; font-weight : bold; }
132+ .ansi-green {
133+ color : # 007f3b ;
134+ font-weight : bold;
135+ background : # ccffdd ;
136+ padding : 2px 5px ;
137+ }
138+
139+ .ansi-red {
140+ color : # d81e05 ;
141+ font-weight : bold;
142+ background : # ffd1d1 ;
143+ padding : 2px 5px ;
144+ }
145+
146+ .ansi-yellow {
147+ color : # ffb81c ;
148+ font-weight : bold;
149+ }
122150
123151 footer {
124152 background : # d8dde0 ;
135163 < div class ="nhs-header-content ">
136164 < svg class ="nhs-logo " xmlns ="http://www.w3.org/2000/svg " viewBox ="0 0 40 16 " aria-hidden ="true ">
137165 < path fill ="#fff " d ="M0 0h40v16H0z "> </ path >
138- < path fill ="#005eb8 " d ="M3.9 1.5h4.4l2.6 9h.1l1.8-9h3.3l-2.8 13H9l-2.7-9h-.1l-1.8 9H1.1M17.3 1.5h3.6l-1 4.9h4L25 1.5h3.5l-2.7 13h-3.5l1.1-5.6h-4.1l-1.2 5.6h-3.4M37.7 4.4c-.7-.3-1.6-.6-2.9-.6-1.4 0-2.5.2-2.5 1.3 0 1.8 5.1 1.2 5.1 5.1 0 3.6-3.3 4.5-6.4 4.5-1.3 0-2.9-.3-4-.7l.8-2.7c.7.4 2.1.7 3.2.7 1.3 0 2.3-.2 2.3-1.4 0-2-5.1-1.2-5.1-5.1 0-4 3.7-4.5 6.4-4.5 1.2 0 2.7.3 3.5.6 "> </ path >
166+ < path fill ="#005eb8 "
167+ d ="M3.9 1.5h4.4l2.6 9h.1l1.8-9h3.3l-2.8 13H9l-2.7-9h-.1l-1.8 9H1.1M17.3 1.5h3.6l-1 4.9h4L25 1.5h3.5l-2.7 13h-3.5l1.1-5.6h-4.1l-1.2 5.6h-3.4M37.7 4.4c-.7-.3-1.6-.6-2.9-.6-1.4 0-2.5.2-2.5 1.3 0 1.8 5.1 1.2 5.1 5.1 0 3.6-3.3 4.5-6.4 4.5-1.3 0-2.9-.3-4-.7l.8-2.7c.7.4 2.1.7 3.2.7 1.3 0 2.3-.2 2.3-1.4 0-2-5.1-1.2-5.1-5.1 0-4 3.7-4.5 6.4-4.5 1.2 0 2.7.3 3.5.6 "> </ path >
139168 </ svg >
140169 </ div >
141170</ header >
@@ -152,7 +181,7 @@ <h1>Campaign Config Validator</h1>
152181 < label for ="jsonfile "> Upload Configuration File (JSON)</ label >
153182 < span style ="display:block; margin-bottom:10px; color:#4c6272; font-size:0.9rem; "> Select the campaign configuration file you wish to validate against the rules API.</ span >
154183
155- < input type ="file " id ="jsonfile " accept =".json " disabled />
184+ < input type ="file " id ="jsonfile " accept =".json " disabled />
156185
157186 < button id ="run " class ="nhs-button " disabled >
158187 Run Validation
@@ -200,11 +229,13 @@ <h3>Visualiser Output</h3>
200229 output . scrollTop = output . scrollHeight ;
201230 }
202231
203- function clearLog ( ) { output . innerHTML = "" ; } // Changed to innerHTML for spans
232+ function clearLog ( ) {
233+ output . innerHTML = "" ;
234+ } // Changed to innerHTML for spans
204235
205- function setStatus ( msg , ready = false ) {
236+ function setStatus ( msg , ready = false ) {
206237 statusText . innerHTML = ready ? `✅ ${ msg } ` : `⏳ ${ msg } ` ;
207- if ( ready ) {
238+ if ( ready ) {
208239 jsonInput . disabled = false ;
209240 statusText . style . borderLeftColor = "#007f3b" ; // Switch the status bar to green
210241 }
@@ -231,8 +262,8 @@ <h3>Visualiser Output</h3>
231262 async function main ( ) {
232263 try {
233264 pyodide = await loadPyodide ( ) ;
234- pyodide . setStdout ( { batched : ( msg ) => log ( msg ) } ) ;
235- pyodide . setStderr ( { batched : ( msg ) => log ( "ERR: " + msg ) } ) ;
265+ pyodide . setStdout ( { batched : ( msg ) => log ( msg ) } ) ;
266+ pyodide . setStderr ( { batched : ( msg ) => log ( "ERR: " + msg ) } ) ;
236267
237268 setStatus ( "Installing Python Libraries..." ) ;
238269 await pyodide . loadPackage ( "micropip" ) ;
@@ -249,10 +280,10 @@ <h3>Visualiser Output</h3>
249280 if ( dir ) {
250281 const parts = dir . split ( '/' ) ;
251282 let current = "" ;
252- for ( const part of parts ) {
283+ for ( const part of parts ) {
253284 current += part + "/" ;
254- if ( ! pyodide . FS . analyzePath ( current . slice ( 0 , - 1 ) ) . exists ) {
255- pyodide . FS . mkdir ( current . slice ( 0 , - 1 ) ) ;
285+ if ( ! pyodide . FS . analyzePath ( current . slice ( 0 , - 1 ) ) . exists ) {
286+ pyodide . FS . mkdir ( current . slice ( 0 , - 1 ) ) ;
256287 }
257288 }
258289 }
@@ -268,6 +299,7 @@ <h3>Visualiser Output</h3>
268299 log ( `❌ FATAL ERROR: ${ err . message } ` ) ;
269300 }
270301 }
302+
271303 main ( ) ;
272304
273305 let configContent = null ;
@@ -284,6 +316,14 @@ <h3>Visualiser Output</h3>
284316 runBtn . addEventListener ( "click" , async ( ) => {
285317 clearLog ( ) ;
286318 log ( "🚀 Validating Configuration...\n" ) ;
319+
320+ try {
321+ JSON . parse ( configContent ) ;
322+ } catch ( err ) {
323+ log ( "❌ JSON Error: " + err . message ) ;
324+ return ;
325+ }
326+
287327 pyodide . globals . set ( "raw_json" , configContent ) ;
288328
289329 try {
0 commit comments