rip-lang 3.10.6 → 3.10.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/docs/RIP-LANG.md +109 -0
- package/docs/dist/rip-ui.min.js +169 -159
- package/docs/dist/rip-ui.min.js.br +0 -0
- package/docs/dist/rip.browser.min.js +170 -160
- package/docs/dist/rip.browser.min.js.br +0 -0
- package/docs/results/images/cover_bg.jpg +0 -0
- package/docs/results/images/crossover.svg +20 -0
- package/docs/results/images/heart.png +0 -0
- package/docs/results/images/human_body.png +0 -0
- package/docs/results/images/pancreas.png +0 -0
- package/docs/results/images/yoga_lady.jpg +0 -0
- package/docs/results/index.html +1117 -0
- package/package.json +1 -1
- package/src/compiler.js +10 -0
- package/src/components.js +151 -53
|
@@ -0,0 +1,1117 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Lab Results</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,500;0,700;1,400&family=Nothing+You+Could+Do&display=swap" rel="stylesheet">
|
|
10
|
+
<script type="module" src="../dist/rip-ui.min.js"></script>
|
|
11
|
+
<style>
|
|
12
|
+
/* ==========================================================================
|
|
13
|
+
Lab Results — Styles
|
|
14
|
+
Single stylesheet: design tokens, app shell, forms, brochure, print
|
|
15
|
+
========================================================================== */
|
|
16
|
+
|
|
17
|
+
:root {
|
|
18
|
+
--color-primary: #3f5e53;
|
|
19
|
+
--color-text: #333;
|
|
20
|
+
--color-base: #333;
|
|
21
|
+
--color-body: #f5f5f5;
|
|
22
|
+
--color-success: #2EAB27;
|
|
23
|
+
--color-danger: #FB2E47;
|
|
24
|
+
--color-border: #e5e5e5;
|
|
25
|
+
--font-base: 'Lato', sans-serif;
|
|
26
|
+
--font-heading: 'Nothing You Could Do', cursive;
|
|
27
|
+
--radius: 4px;
|
|
28
|
+
--page-width: 800px;
|
|
29
|
+
--page-height: 1056px;
|
|
30
|
+
--shadow-panel: 0 6px 20px 0 rgba(50,50,93,.06), 0 2px 4px 0 rgba(0,0,0,.03);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
34
|
+
|
|
35
|
+
html { font-size: 10px; }
|
|
36
|
+
@media (max-width: 1200px) { html { font-size: 9px; } }
|
|
37
|
+
|
|
38
|
+
body {
|
|
39
|
+
margin: 0;
|
|
40
|
+
min-height: 100vh;
|
|
41
|
+
font-family: var(--font-base);
|
|
42
|
+
font-size: 1.6rem;
|
|
43
|
+
line-height: 1.4;
|
|
44
|
+
color: var(--color-base);
|
|
45
|
+
background: var(--color-body);
|
|
46
|
+
-webkit-font-smoothing: antialiased;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* --- App Shell --- */
|
|
50
|
+
|
|
51
|
+
.app { padding-bottom: 2rem; }
|
|
52
|
+
|
|
53
|
+
@media (min-width: 1300px) {
|
|
54
|
+
.app {
|
|
55
|
+
padding: 3rem;
|
|
56
|
+
display: flex;
|
|
57
|
+
justify-content: center;
|
|
58
|
+
align-items: flex-start;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.sidebar {
|
|
63
|
+
display: none;
|
|
64
|
+
background: white;
|
|
65
|
+
padding: 3rem;
|
|
66
|
+
position: sticky;
|
|
67
|
+
top: 3rem;
|
|
68
|
+
box-shadow: var(--shadow-panel);
|
|
69
|
+
border-radius: var(--radius);
|
|
70
|
+
max-width: 40rem;
|
|
71
|
+
max-height: 90vh;
|
|
72
|
+
overflow-y: auto;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@media (min-width: 1300px) { .sidebar { display: block; } }
|
|
76
|
+
|
|
77
|
+
.sidebar__heading {
|
|
78
|
+
font-size: 1.8rem;
|
|
79
|
+
font-weight: 600;
|
|
80
|
+
margin-bottom: 2.5rem;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.header {
|
|
84
|
+
position: fixed;
|
|
85
|
+
top: 0; left: 0; right: 0;
|
|
86
|
+
display: flex;
|
|
87
|
+
justify-content: space-between;
|
|
88
|
+
align-items: center;
|
|
89
|
+
padding: 1rem 0;
|
|
90
|
+
background: white;
|
|
91
|
+
box-shadow: 0 1px 8px rgba(0,0,0,.15);
|
|
92
|
+
z-index: 10;
|
|
93
|
+
height: 5rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@media (min-width: 1300px) { .header { display: none; } }
|
|
97
|
+
|
|
98
|
+
.header__toggle {
|
|
99
|
+
padding: 0.5rem 1rem;
|
|
100
|
+
margin-left: 1rem;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
font-size: 1.9rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.header__title { font-size: 1.6rem; font-weight: 600; }
|
|
106
|
+
|
|
107
|
+
.drawer-overlay {
|
|
108
|
+
display: none;
|
|
109
|
+
position: fixed;
|
|
110
|
+
inset: 0;
|
|
111
|
+
background: rgba(0,0,0,.4);
|
|
112
|
+
z-index: 20;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.drawer-overlay.open { display: block; }
|
|
116
|
+
|
|
117
|
+
.drawer {
|
|
118
|
+
position: fixed;
|
|
119
|
+
top: 0; left: 0; bottom: 0;
|
|
120
|
+
width: 320px;
|
|
121
|
+
background: white;
|
|
122
|
+
z-index: 30;
|
|
123
|
+
padding: 2rem;
|
|
124
|
+
overflow-y: auto;
|
|
125
|
+
transform: translateX(-100%);
|
|
126
|
+
transition: transform 0.25s ease;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@media (min-width: 550px) { .drawer { width: 400px; } }
|
|
130
|
+
|
|
131
|
+
.drawer.open { transform: translateX(0); }
|
|
132
|
+
|
|
133
|
+
.preview { padding-top: 6rem; }
|
|
134
|
+
|
|
135
|
+
@media (min-width: 1300px) {
|
|
136
|
+
.preview { padding: 0; margin-left: 4rem; }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* --- Settings Form --- */
|
|
140
|
+
|
|
141
|
+
.settings__section { margin-top: 3rem; }
|
|
142
|
+
.settings__section:first-child { margin-top: 0; }
|
|
143
|
+
|
|
144
|
+
.settings__heading {
|
|
145
|
+
font-weight: 600;
|
|
146
|
+
font-size: 1.4rem;
|
|
147
|
+
text-transform: uppercase;
|
|
148
|
+
color: rgba(51,51,51,.8);
|
|
149
|
+
margin-bottom: 1.8rem;
|
|
150
|
+
letter-spacing: 0.3px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.settings__fields {
|
|
154
|
+
display: flex;
|
|
155
|
+
flex-wrap: wrap;
|
|
156
|
+
gap: 1.2rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.settings__field {
|
|
160
|
+
flex: 1 1 45%;
|
|
161
|
+
min-width: 140px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.settings__field label {
|
|
165
|
+
display: block;
|
|
166
|
+
font-size: 1.3rem;
|
|
167
|
+
font-weight: 500;
|
|
168
|
+
color: rgba(51,51,51,.7);
|
|
169
|
+
margin-bottom: 0.4rem;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.settings__field input,
|
|
173
|
+
.settings__field select {
|
|
174
|
+
width: 100%;
|
|
175
|
+
padding: 1rem 1.2rem;
|
|
176
|
+
font-family: var(--font-base);
|
|
177
|
+
font-size: 1.5rem;
|
|
178
|
+
font-weight: 500;
|
|
179
|
+
border: 1px solid var(--color-border);
|
|
180
|
+
border-radius: var(--radius);
|
|
181
|
+
outline: none;
|
|
182
|
+
transition: border-color 0.15s;
|
|
183
|
+
color: var(--color-text);
|
|
184
|
+
background: white;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.settings__field input:focus,
|
|
188
|
+
.settings__field select:focus {
|
|
189
|
+
border-color: var(--color-primary);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.btn-primary {
|
|
193
|
+
display: block;
|
|
194
|
+
width: 100%;
|
|
195
|
+
margin-top: 3rem;
|
|
196
|
+
padding: 1.2rem;
|
|
197
|
+
font-family: var(--font-base);
|
|
198
|
+
font-size: 1.55rem;
|
|
199
|
+
font-weight: 800;
|
|
200
|
+
color: white;
|
|
201
|
+
background: var(--color-primary);
|
|
202
|
+
border: none;
|
|
203
|
+
border-radius: var(--radius);
|
|
204
|
+
cursor: pointer;
|
|
205
|
+
transition: background 0.15s;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.btn-primary:hover { background: #355247; }
|
|
209
|
+
|
|
210
|
+
/* --- Brochure Pages --- */
|
|
211
|
+
|
|
212
|
+
.brochure {
|
|
213
|
+
width: var(--page-width);
|
|
214
|
+
margin: 0 auto;
|
|
215
|
+
box-shadow: var(--shadow-panel);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.brochure__page {
|
|
219
|
+
font-size: 15px;
|
|
220
|
+
width: var(--page-width);
|
|
221
|
+
min-height: var(--page-height);
|
|
222
|
+
background-size: cover;
|
|
223
|
+
background-repeat: no-repeat;
|
|
224
|
+
border-top: 2px solid rgba(0,0,0,.1);
|
|
225
|
+
position: relative;
|
|
226
|
+
overflow: hidden;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.brochure__page:first-child { border-top: 0; }
|
|
230
|
+
|
|
231
|
+
.page-cover {
|
|
232
|
+
display: flex;
|
|
233
|
+
flex-direction: column;
|
|
234
|
+
justify-content: space-between;
|
|
235
|
+
height: var(--page-height);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.cover__heading-fade {
|
|
239
|
+
width: 100%;
|
|
240
|
+
height: 280px;
|
|
241
|
+
background: linear-gradient(white, rgba(255,255,255,0));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.cover__title {
|
|
245
|
+
font-family: var(--font-heading);
|
|
246
|
+
font-size: 50px;
|
|
247
|
+
letter-spacing: 1.5px;
|
|
248
|
+
line-height: 60px;
|
|
249
|
+
padding: 60px 0 0 60px;
|
|
250
|
+
width: 380px;
|
|
251
|
+
text-transform: uppercase;
|
|
252
|
+
font-weight: 700;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.cover__body {
|
|
256
|
+
background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,.7));
|
|
257
|
+
flex: 1;
|
|
258
|
+
display: flex;
|
|
259
|
+
flex-direction: column;
|
|
260
|
+
justify-content: flex-end;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.cover__copy {
|
|
264
|
+
font-size: 20px;
|
|
265
|
+
margin: 0 0 60px 60px;
|
|
266
|
+
color: white;
|
|
267
|
+
text-shadow: 1px 1px 5px rgba(0,0,0,.3);
|
|
268
|
+
width: 440px;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.cover__footer {
|
|
272
|
+
display: flex;
|
|
273
|
+
justify-content: space-between;
|
|
274
|
+
align-items: center;
|
|
275
|
+
padding: 15px 40px;
|
|
276
|
+
background: rgba(255,255,255,.9);
|
|
277
|
+
backdrop-filter: blur(10px);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.cover__footer img { width: 135px; }
|
|
281
|
+
|
|
282
|
+
.cover__powered-by {
|
|
283
|
+
opacity: 0.8;
|
|
284
|
+
font-weight: 600;
|
|
285
|
+
color: rgba(51,51,51,.9);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.cover__powered-by div:first-child { font-size: 15px; }
|
|
289
|
+
.cover__powered-by div:last-child { font-size: 17px; margin-top: -3px; }
|
|
290
|
+
|
|
291
|
+
.page-summary { padding: 30px; }
|
|
292
|
+
|
|
293
|
+
.page-summary .page-heading {
|
|
294
|
+
color: var(--color-primary);
|
|
295
|
+
font-size: 20px;
|
|
296
|
+
letter-spacing: 0.5px;
|
|
297
|
+
font-weight: 600;
|
|
298
|
+
text-transform: uppercase;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.summary__intro {
|
|
302
|
+
margin-top: 30px;
|
|
303
|
+
display: flex;
|
|
304
|
+
justify-content: space-between;
|
|
305
|
+
align-items: center;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.summary__patient {
|
|
309
|
+
background: white;
|
|
310
|
+
border: 1px solid rgba(63,94,83,.6);
|
|
311
|
+
padding: 20px;
|
|
312
|
+
border-radius: var(--radius);
|
|
313
|
+
flex-shrink: 0;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.summary__patient-name { font-weight: 700; font-size: 17px; }
|
|
317
|
+
.summary__patient-meta { margin-top: 2px; color: rgba(51,51,51,.8); }
|
|
318
|
+
|
|
319
|
+
.summary__patient-lab { margin-top: 15px; color: rgba(51,51,51,.8); }
|
|
320
|
+
.summary__patient-lab strong { font-weight: 600; color: rgba(51,51,51,.9); }
|
|
321
|
+
|
|
322
|
+
.summary__copy {
|
|
323
|
+
flex: 1;
|
|
324
|
+
margin-left: 40px;
|
|
325
|
+
line-height: 1.5;
|
|
326
|
+
color: rgba(51,51,51,.8);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.summary-table {
|
|
330
|
+
width: 100%;
|
|
331
|
+
margin-top: 15px;
|
|
332
|
+
border-spacing: 0;
|
|
333
|
+
border-collapse: collapse;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.summary-table:first-of-type { margin-top: 20px; }
|
|
337
|
+
|
|
338
|
+
.summary-table thead th {
|
|
339
|
+
font-weight: 800;
|
|
340
|
+
font-size: 12pt;
|
|
341
|
+
padding: 10px 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.summary-table tbody {
|
|
345
|
+
background: white;
|
|
346
|
+
border: 1px solid #f0f0f0;
|
|
347
|
+
border-radius: var(--radius);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.summary-table tbody tr { border-bottom: 1px solid #f0f0f0; }
|
|
351
|
+
.summary-table tbody tr:first-child,
|
|
352
|
+
.summary-table tbody tr:last-child { border: none; }
|
|
353
|
+
|
|
354
|
+
.summary-table tbody tr:first-child td {
|
|
355
|
+
padding-top: 20px;
|
|
356
|
+
padding-bottom: 0;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.summary-table tbody tr:first-child td:first-child {
|
|
360
|
+
font-size: 14px;
|
|
361
|
+
font-weight: 700;
|
|
362
|
+
color: rgba(51,51,51,.7);
|
|
363
|
+
text-transform: uppercase;
|
|
364
|
+
letter-spacing: 0.2px;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.summary-table td {
|
|
368
|
+
text-align: center;
|
|
369
|
+
padding: 15px 25px;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.summary-table td:first-child { text-align: left; width: 320px; }
|
|
373
|
+
.summary-table td:nth-child(2) { font-weight: 600; }
|
|
374
|
+
|
|
375
|
+
.summary-table td .name { font-size: 17px; font-weight: 600; }
|
|
376
|
+
.summary-table td .desc { font-size: 14px; color: rgba(51,51,51,.7); }
|
|
377
|
+
|
|
378
|
+
.summary-table td.value {
|
|
379
|
+
color: rgba(51,51,51,.8);
|
|
380
|
+
font-size: 20pt;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.row-acceptable td:first-child,
|
|
384
|
+
.row-acceptable td:nth-child(2) { color: var(--color-success); }
|
|
385
|
+
|
|
386
|
+
.row-unacceptable td:first-child,
|
|
387
|
+
.row-unacceptable td:nth-child(2) { color: var(--color-danger); }
|
|
388
|
+
|
|
389
|
+
.row-acceptable td:first-child,
|
|
390
|
+
.row-unacceptable td:first-child {
|
|
391
|
+
display: flex;
|
|
392
|
+
align-items: center;
|
|
393
|
+
gap: 12px;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.status-icon {
|
|
397
|
+
width: 20px;
|
|
398
|
+
height: 20px;
|
|
399
|
+
flex-shrink: 0;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.page-detail {
|
|
403
|
+
display: flex;
|
|
404
|
+
flex-direction: column;
|
|
405
|
+
min-height: var(--page-height);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.detail__header {
|
|
409
|
+
flex-shrink: 0;
|
|
410
|
+
color: white;
|
|
411
|
+
display: flex;
|
|
412
|
+
justify-content: center;
|
|
413
|
+
align-items: center;
|
|
414
|
+
padding: 30px 40px;
|
|
415
|
+
min-height: 280px;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.detail__header-image {
|
|
419
|
+
background-size: contain;
|
|
420
|
+
background-repeat: no-repeat;
|
|
421
|
+
background-position: center;
|
|
422
|
+
flex-shrink: 0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.detail__header-text { margin-left: 70px; max-width: 360px; }
|
|
426
|
+
|
|
427
|
+
.detail__header-title {
|
|
428
|
+
font-size: 18px;
|
|
429
|
+
font-weight: 700;
|
|
430
|
+
text-transform: uppercase;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
.detail__header-copy {
|
|
434
|
+
color: rgba(255,255,255,.85);
|
|
435
|
+
margin-top: 20px;
|
|
436
|
+
line-height: 1.5;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.detail__content {
|
|
440
|
+
display: flex;
|
|
441
|
+
padding: 25px;
|
|
442
|
+
flex: 1;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.detail__item {
|
|
446
|
+
width: 50%;
|
|
447
|
+
background: white;
|
|
448
|
+
border: 1px solid #f0f0f0;
|
|
449
|
+
border-radius: var(--radius);
|
|
450
|
+
padding: 25px;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.detail__item + .detail__item { margin-left: 25px; }
|
|
454
|
+
|
|
455
|
+
.detail__item-header {
|
|
456
|
+
margin-top: 15px;
|
|
457
|
+
display: flex;
|
|
458
|
+
align-items: center;
|
|
459
|
+
gap: 12px;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.detail__item-header .name { font-size: 18px; font-weight: 600; }
|
|
463
|
+
.detail__item-header .desc { font-size: 15px; color: rgba(51,51,51,.7); }
|
|
464
|
+
|
|
465
|
+
.detail__item-header.acceptable { color: var(--color-success); }
|
|
466
|
+
.detail__item-header.unacceptable { color: var(--color-danger); }
|
|
467
|
+
|
|
468
|
+
.detail__item-info {
|
|
469
|
+
margin-top: 20px;
|
|
470
|
+
color: rgba(51,51,51,.9);
|
|
471
|
+
line-height: 1.5;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.detail__item-info p { margin: 1rem 0; }
|
|
475
|
+
.detail__item-info p:first-child { margin-top: 0; }
|
|
476
|
+
.detail__item-info strong { font-weight: 700; }
|
|
477
|
+
|
|
478
|
+
.detail__maintenance { margin-top: 25px; }
|
|
479
|
+
|
|
480
|
+
.detail__maintenance-heading {
|
|
481
|
+
text-transform: uppercase;
|
|
482
|
+
font-weight: 600;
|
|
483
|
+
font-size: 14px;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
.detail__maintenance-item {
|
|
487
|
+
margin-top: 15px;
|
|
488
|
+
display: flex;
|
|
489
|
+
align-items: baseline;
|
|
490
|
+
gap: 12px;
|
|
491
|
+
color: rgba(51,51,51,.9);
|
|
492
|
+
line-height: 1.4;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.detail__maintenance-icon {
|
|
496
|
+
width: 16px;
|
|
497
|
+
flex-shrink: 0;
|
|
498
|
+
display: flex;
|
|
499
|
+
justify-content: center;
|
|
500
|
+
color: rgba(51,51,51,.5);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.gauge {
|
|
504
|
+
position: relative;
|
|
505
|
+
display: inline-block;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.gauge--flip svg { transform: scaleX(-1); }
|
|
509
|
+
|
|
510
|
+
.gauge__needle {
|
|
511
|
+
position: absolute;
|
|
512
|
+
width: 131px;
|
|
513
|
+
height: 131px;
|
|
514
|
+
margin: 0 auto;
|
|
515
|
+
border-radius: 50%;
|
|
516
|
+
top: 21px;
|
|
517
|
+
left: 0;
|
|
518
|
+
right: 0;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.gauge__arrow,
|
|
522
|
+
.gauge__arrow-border {
|
|
523
|
+
position: absolute;
|
|
524
|
+
margin: 0 auto;
|
|
525
|
+
left: 0; right: 0;
|
|
526
|
+
width: 0; height: 0;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.gauge__arrow {
|
|
530
|
+
top: -13px;
|
|
531
|
+
z-index: 2;
|
|
532
|
+
border-left: 15px solid transparent;
|
|
533
|
+
border-right: 15px solid transparent;
|
|
534
|
+
border-bottom: 15px solid var(--color-success);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.gauge__arrow-border {
|
|
538
|
+
top: -15px;
|
|
539
|
+
z-index: 1;
|
|
540
|
+
border-left: 15px solid transparent;
|
|
541
|
+
border-right: 15px solid transparent;
|
|
542
|
+
border-bottom: 15px solid white;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.gauge__value,
|
|
546
|
+
.gauge__units {
|
|
547
|
+
font-weight: 600;
|
|
548
|
+
position: absolute;
|
|
549
|
+
left: 0; right: 0;
|
|
550
|
+
text-align: center;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.gauge__value { font-size: 18px; color: white; bottom: 26px; }
|
|
554
|
+
.gauge__units { font-size: 13px; color: rgba(255,255,255,.9); bottom: 13px; }
|
|
555
|
+
|
|
556
|
+
@media print {
|
|
557
|
+
body { background: white; }
|
|
558
|
+
|
|
559
|
+
.header, .sidebar, .drawer, .drawer-overlay, .btn-primary,
|
|
560
|
+
.settings__section { display: none !important; }
|
|
561
|
+
|
|
562
|
+
.app { padding: 0; display: block; }
|
|
563
|
+
.preview { padding: 0; margin: 0; }
|
|
564
|
+
|
|
565
|
+
.brochure { width: 100%; box-shadow: none; }
|
|
566
|
+
|
|
567
|
+
.brochure__page {
|
|
568
|
+
width: 100%;
|
|
569
|
+
height: 100%;
|
|
570
|
+
position: absolute;
|
|
571
|
+
top: 0; left: 0;
|
|
572
|
+
margin: auto;
|
|
573
|
+
border: none;
|
|
574
|
+
page-break-before: always;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.brochure__page:first-child { page-break-before: auto; }
|
|
578
|
+
}
|
|
579
|
+
</style>
|
|
580
|
+
</head>
|
|
581
|
+
<body>
|
|
582
|
+
|
|
583
|
+
<!-- ===== Components ===== -->
|
|
584
|
+
|
|
585
|
+
<script type="text/rip" data-name="index">
|
|
586
|
+
# Lab Results - Main Page
|
|
587
|
+
|
|
588
|
+
export Home = component
|
|
589
|
+
appTitle := 'Lab Results'
|
|
590
|
+
title := 'My Guide to Health'
|
|
591
|
+
poweredBy := 'Crossover Health'
|
|
592
|
+
lab := 'Medical Diagnostics'
|
|
593
|
+
|
|
594
|
+
firstName := 'Scott'
|
|
595
|
+
lastName := 'Bowman'
|
|
596
|
+
age := 42
|
|
597
|
+
gender := 'Male'
|
|
598
|
+
|
|
599
|
+
history := [
|
|
600
|
+
date: '2022-10'
|
|
601
|
+
triglycerides: 181
|
|
602
|
+
hdlCholesterol: 44
|
|
603
|
+
totalCholesterol: 177
|
|
604
|
+
cholesterolHdlRatio: 4.3
|
|
605
|
+
glucose: 75
|
|
606
|
+
a1c: 5.2
|
|
607
|
+
]
|
|
608
|
+
|
|
609
|
+
ranges :=
|
|
610
|
+
triglycerides:
|
|
611
|
+
name: 'Triglycerides'
|
|
612
|
+
range: ['<', 150]
|
|
613
|
+
units: 'mg/dL'
|
|
614
|
+
desc: 'Reference range: < 150 mg/dL'
|
|
615
|
+
info:
|
|
616
|
+
acceptable: 'Your result falls within the normal Reference Range.'
|
|
617
|
+
unacceptable: 'Your result falls outside the normal Reference Range.'
|
|
618
|
+
hdlCholesterol:
|
|
619
|
+
name: 'HDL Cholesterol'
|
|
620
|
+
range: ['>=', 40]
|
|
621
|
+
units: 'mg/dL'
|
|
622
|
+
desc: 'Reference range: ≥ 40 mg/dL'
|
|
623
|
+
info:
|
|
624
|
+
acceptable: 'Your result falls within the normal Reference Range.'
|
|
625
|
+
unacceptable: 'Your result falls outside the normal Reference Range.'
|
|
626
|
+
totalCholesterol:
|
|
627
|
+
name: 'Total Cholesterol'
|
|
628
|
+
range: ['<>', 125, 199]
|
|
629
|
+
units: 'mg/dL'
|
|
630
|
+
desc: 'Reference range: 125-199 mg/dL'
|
|
631
|
+
info:
|
|
632
|
+
acceptable: 'Your result falls within the normal Reference Range.'
|
|
633
|
+
unacceptable: 'Your result falls outside the normal Reference Range.'
|
|
634
|
+
cholesterolHdlRatio:
|
|
635
|
+
name: 'Cholesterol / HDL Ratio'
|
|
636
|
+
range: ['<=', 5]
|
|
637
|
+
units: '(calc)'
|
|
638
|
+
desc: 'Reference range: ≤ 5.0 (calc)'
|
|
639
|
+
info:
|
|
640
|
+
acceptable: 'Your result falls within the normal Reference Range.'
|
|
641
|
+
unacceptable: 'Your result falls outside the normal Reference Range.'
|
|
642
|
+
glucose:
|
|
643
|
+
name: 'Glucose'
|
|
644
|
+
range: ['<>', 65, 99]
|
|
645
|
+
units: 'mg/dL'
|
|
646
|
+
desc: 'Reference range: 65-99 mg/dL'
|
|
647
|
+
info:
|
|
648
|
+
acceptable: 'Your result falls within the normal Reference Range.'
|
|
649
|
+
unacceptable: 'Your result falls outside the normal Reference Range.'
|
|
650
|
+
a1c:
|
|
651
|
+
name: 'Hemoglobin A1c'
|
|
652
|
+
range: ['<', 5.7]
|
|
653
|
+
units: '%'
|
|
654
|
+
desc: 'Reference range: < 5.7%'
|
|
655
|
+
info:
|
|
656
|
+
acceptable: 'Your result falls within the normal Reference Range.'
|
|
657
|
+
unacceptable: 'Your result falls outside the normal Reference Range.'
|
|
658
|
+
waistCircumference:
|
|
659
|
+
name: 'Waist Circumference'
|
|
660
|
+
range: ['<=', 40]
|
|
661
|
+
units: 'in'
|
|
662
|
+
desc: 'Reference range: ≤ 40 in'
|
|
663
|
+
info:
|
|
664
|
+
acceptable: 'Your result falls within the normal Reference Range.'
|
|
665
|
+
unacceptable: 'Your result falls outside the normal Reference Range.'
|
|
666
|
+
bloodPressure:
|
|
667
|
+
name: 'Blood Pressure'
|
|
668
|
+
range: ['<', '120/80']
|
|
669
|
+
units: 'mm/Hg'
|
|
670
|
+
desc: 'Reference range: < 120/80 mmHg'
|
|
671
|
+
info:
|
|
672
|
+
acceptable: 'Your result falls within the normal Reference Range.'
|
|
673
|
+
unacceptable: 'Your result falls outside the normal Reference Range.'
|
|
674
|
+
|
|
675
|
+
acceptable: (key, value) ->
|
|
676
|
+
ref = ranges[key]
|
|
677
|
+
return 'acceptable' unless ref
|
|
678
|
+
r = ref.range
|
|
679
|
+
ok = false
|
|
680
|
+
if typeof r[1] is 'string' and r[1].includes('/')
|
|
681
|
+
bp = r[1].split('/')
|
|
682
|
+
bv = String(value).split('/')
|
|
683
|
+
ok = Number(bv[0]) <= Number(bp[0]) and Number(bv[1]) <= Number(bp[1])
|
|
684
|
+
else
|
|
685
|
+
switch r[0]
|
|
686
|
+
when '<' then ok = value < r[1]
|
|
687
|
+
when '<=' then ok = value <= r[1]
|
|
688
|
+
when '>=' then ok = value >= r[1]
|
|
689
|
+
when '<>' then ok = value >= r[1] and value <= r[2]
|
|
690
|
+
if ok then 'acceptable' else 'unacceptable'
|
|
691
|
+
|
|
692
|
+
topics := [
|
|
693
|
+
{ title: 'Heart Health', gradient: 'linear-gradient(45deg, #851717, #E83F3F)', image: 'images/heart.png', imageWidth: 166, imageHeight: 240, copy: "Your heart is one of the most important organs in your body. Every day, it beats around 100,000 times, pumping blood through an extensive network of blood vessels.\n\nIt's responsible for supplying oxygen to your body, removing waste materials, supplying energy and delivering immune system responses. Given all these essential functions, it's important to keep your heart healthy.", biomarkers: [{ key: 'triglycerides', text: '<strong>Triglycerides</strong> are fats composed of fatty acids and glycerol. The level of triglycerides in your blood tells how well your body processes the fat in your diet.', extra: 'Accurate results require fasting for nine to twelve hours (no food or drink except water and medication) prior to testing.', tips: [{ icon: 'utensils', text: 'Drink watery drinks instead of sugary drinks.' }, { icon: 'drop', text: 'Choose fish rich in omega-3 fatty acids to lower your triglycerides.' }] }, { key: 'hdlCholesterol', text: '<strong>High Density Lipoprotein (HDL) cholesterol</strong> is commonly called "good" cholesterol. Unlike other cholesterol levels, the HDL cholesterol test result is best if it is high. Elevated HDL cholesterol is associated with decreased risk of heart disease.', tips: [{ icon: 'utensils', text: 'Try adding almonds or walnuts to hot or cold cereal for extra crunch and some healthy fat.' }, { icon: 'drop', text: 'Choose a margarine spread without hydrogenated or partially-hydrogenated oils.' }] }] }
|
|
694
|
+
{ title: 'Heart Health', isImagePage: true, image: 'images/yoga_lady.jpg', biomarkers: [{ key: 'totalCholesterol', text: '<strong>Total Cholesterol</strong> is a combination of three types of cholesterol: HDL, LDL, and part of triglycerides.', extra: 'High cholesterol may put you at risk for heart disease or stroke. A low cholesterol measurement can indicate other health conditions.', tips: [{ icon: 'walk', text: 'Keep it interesting. Try new exercise activities to improve your overall fitness and prevent boredom.' }, { icon: 'utensils', text: 'Choose oatmeal, whole-wheat toast, or a whole-grain English muffin instead of a doughnut or pastry at breakfast.' }] }, { key: 'cholesterolHdlRatio', text: '<strong>Total cholesterol/HDL cholesterol ratio</strong> is a calculation obtained by dividing the total cholesterol level by the HDL cholesterol level and is another indicator of heart disease risk. A ratio of 5.0 or less is associated with a lower risk of heart disease. A ratio of less than 3.5 is highly desirable.', tips: [{ icon: 'utensils', text: 'Go for the whole grains. Try brown rice or whole-wheat pasta. Switch from white bread to whole-wheat bread.' }, { icon: 'drop', text: 'Use liquid fats instead of solid fats (such as shortening) in your cooking and baking.' }] }] }
|
|
695
|
+
{ title: 'Diabetes Risk', gradient: 'linear-gradient(45deg, #5C3619, #E8873F)', image: 'images/pancreas.png', imageWidth: 233, imageHeight: 149, copy: "The pancreas is a relatively small organ located right behind your stomach. It has two main functions that help your body convert the food you eat into fuel. The exocrine function aids in digestion while the endocrine function creates and releases hormones to regulate your blood sugar.\n\nBecause of these critical roles, your pancreas can be tied to several serious health issues.", biomarkers: [{ key: 'glucose', text: '<strong>Glucose</strong> ("blood sugar") is the chief source of energy for all cells in the body. Glucose levels are regulated by hormones produced by your pancreas, including insulin.', tips: [{ icon: 'walk', text: 'Boost your metabolism with strength training. Strength training can lower glucose levels by increasing lean muscle and reducing body fat.' }, { icon: 'utensils', text: 'Carbohydrates count. Choose from healthy carbohydrates, such as whole grains, fruits, vegetables, legumes (beans/peas) and low-fat or fat-free milk and yogurt.' }] }, { key: 'a1c', text: "<strong>Hemoglobin A1c</strong> measures the percentage of glucose that's bound to hemoglobin, a protein found in red blood cells whose job is to carry oxygen throughout your body.", tips: [{ icon: 'doctor', text: 'Check your glucose as directed. Work with your doctor to determine if, and how often, you should check your blood sugar.' }, { icon: 'utensils', text: 'Eat a balanced diet. Load up on non-starchy vegetables, but be mindful of serving sizes when eating fruits, protein and carbohydrates.' }] }] }
|
|
696
|
+
{ title: 'Physical Measures', gradient: 'linear-gradient(45deg, #214B7A, #3F8EE8)', image: 'images/human_body.png', imageWidth: 140, imageHeight: 247, copy: "During your screening, physical measurements were taken to provide you with more information about your health. These measures are considered risk factors for chronic health conditions, like heart disease, diabetes and stroke.\n\nThese measures should be used with all of your blood tests to understand your risk for these conditions.", conditional: 'waistCircumference', biomarkers: [{ key: 'waistCircumference', text: '<strong>Waist circumference</strong> measures the stored fat around your waist area, also known as "abdominal obesity" or "having an apple shape". It can provide a different look at your weight related health risk than a body mass index (BMI).', tips: [{ icon: 'walk', text: 'Did you know that walking is a great way to reduce belly fat and strengthen the muscles in the lower back?' }, { icon: 'utensils', text: 'Eat at home more and dine out less. Strive to dine out no more than once or twice each week.' }] }, { key: 'bloodPressure', text: '<strong>Blood pressure</strong> is the force of blood pushing against the artery walls as the heart pumps blood. Having high blood pressure can damage your heart and lead to other health problems such as heart disease and stroke.', tips: [{ icon: 'walk', text: 'Aerobic exercise lowers the blood pressure by strengthening the heart and the blood vessels.' }, { icon: 'smile', text: 'Try a relaxation technique, such as deep breathing or meditation.' }] }] }
|
|
697
|
+
]
|
|
698
|
+
|
|
699
|
+
drawerOpen := false
|
|
700
|
+
mobile := window.matchMedia('(max-width: 1299px)').matches
|
|
701
|
+
|
|
702
|
+
setupResize: ->
|
|
703
|
+
mq = window.matchMedia('(max-width: 1299px)')
|
|
704
|
+
handler = (e) ->
|
|
705
|
+
mobile = e.matches
|
|
706
|
+
drawerOpen = false
|
|
707
|
+
mq.addEventListener 'change', handler
|
|
708
|
+
|
|
709
|
+
~> @setupResize()
|
|
710
|
+
|
|
711
|
+
render
|
|
712
|
+
.app
|
|
713
|
+
if mobile
|
|
714
|
+
.header
|
|
715
|
+
.header__toggle
|
|
716
|
+
@click: -> drawerOpen = true
|
|
717
|
+
span "☰"
|
|
718
|
+
.header__title appTitle
|
|
719
|
+
div style: 'width: 48px'
|
|
720
|
+
div
|
|
721
|
+
class: "drawer-overlay #{if drawerOpen then 'open' else ''}"
|
|
722
|
+
@click: -> drawerOpen = false
|
|
723
|
+
div
|
|
724
|
+
class: "drawer #{if drawerOpen then 'open' else ''}"
|
|
725
|
+
Settings
|
|
726
|
+
firstName: firstName, lastName: lastName, age: age, gender: gender
|
|
727
|
+
history: history, ranges: ranges
|
|
728
|
+
|
|
729
|
+
.sidebar
|
|
730
|
+
.sidebar__heading appTitle
|
|
731
|
+
Settings
|
|
732
|
+
firstName: firstName, lastName: lastName, age: age, gender: gender
|
|
733
|
+
history: history, ranges: ranges
|
|
734
|
+
|
|
735
|
+
.preview
|
|
736
|
+
Brochure
|
|
737
|
+
title: title, poweredBy: poweredBy, lab: lab
|
|
738
|
+
firstName: firstName, lastName: lastName, age: age, gender: gender
|
|
739
|
+
history: history, ranges: ranges, topics: topics
|
|
740
|
+
acceptable: (k, v) -> @acceptable(k, v)
|
|
741
|
+
</script>
|
|
742
|
+
|
|
743
|
+
<script type="text/rip" data-name="settings">
|
|
744
|
+
# Settings - Form panel with two-way bound inputs
|
|
745
|
+
|
|
746
|
+
export Settings = component
|
|
747
|
+
@firstName := ''
|
|
748
|
+
@lastName := ''
|
|
749
|
+
@age := 0
|
|
750
|
+
@gender := 'Male'
|
|
751
|
+
@history := []
|
|
752
|
+
@ranges := {}
|
|
753
|
+
|
|
754
|
+
render
|
|
755
|
+
div
|
|
756
|
+
.settings__section
|
|
757
|
+
.settings__heading "Patient Info"
|
|
758
|
+
.settings__fields
|
|
759
|
+
.settings__field
|
|
760
|
+
label "First name"
|
|
761
|
+
input type: 'text', value <=> firstName
|
|
762
|
+
.settings__field
|
|
763
|
+
label "Last name"
|
|
764
|
+
input type: 'text', value <=> lastName
|
|
765
|
+
.settings__field
|
|
766
|
+
label "Age"
|
|
767
|
+
input type: 'number', value <=> age
|
|
768
|
+
.settings__field
|
|
769
|
+
label "Gender"
|
|
770
|
+
select
|
|
771
|
+
@change: (e) -> gender = e.target.value
|
|
772
|
+
option value: 'Male', selected: gender is 'Male', "Male"
|
|
773
|
+
option value: 'Female', selected: gender is 'Female', "Female"
|
|
774
|
+
|
|
775
|
+
.settings__section
|
|
776
|
+
.settings__heading "Heart Health"
|
|
777
|
+
.settings__fields
|
|
778
|
+
.settings__field
|
|
779
|
+
label "Triglycerides (mg/dL)"
|
|
780
|
+
input type: 'number', value <=> history[0].triglycerides
|
|
781
|
+
.settings__field
|
|
782
|
+
label "HDL Cholesterol (mg/dL)"
|
|
783
|
+
input type: 'number', value <=> history[0].hdlCholesterol
|
|
784
|
+
.settings__field
|
|
785
|
+
label "Total Cholesterol (mg/dL)"
|
|
786
|
+
input type: 'number', value <=> history[0].totalCholesterol
|
|
787
|
+
.settings__field
|
|
788
|
+
label "Cholesterol/HDL Ratio"
|
|
789
|
+
input type: 'number', step: '0.1', value <=> history[0].cholesterolHdlRatio
|
|
790
|
+
|
|
791
|
+
.settings__section
|
|
792
|
+
.settings__heading "Pancreas Health"
|
|
793
|
+
.settings__fields
|
|
794
|
+
.settings__field
|
|
795
|
+
label "Glucose (mg/dL)"
|
|
796
|
+
input type: 'number', value <=> history[0].glucose
|
|
797
|
+
.settings__field
|
|
798
|
+
label "Hemoglobin A1c (%)"
|
|
799
|
+
input type: 'number', step: '0.1', value <=> history[0].a1c
|
|
800
|
+
|
|
801
|
+
button.btn-primary "Print PDF"
|
|
802
|
+
@click: -> window.print()
|
|
803
|
+
</script>
|
|
804
|
+
|
|
805
|
+
<script type="text/rip" data-name="brochure">
|
|
806
|
+
# Brochure — Container composing all pages
|
|
807
|
+
|
|
808
|
+
export Brochure = component
|
|
809
|
+
@title := ''
|
|
810
|
+
@poweredBy := ''
|
|
811
|
+
@lab := ''
|
|
812
|
+
@firstName := ''
|
|
813
|
+
@lastName := ''
|
|
814
|
+
@age := 0
|
|
815
|
+
@gender := 'Male'
|
|
816
|
+
@history := []
|
|
817
|
+
@ranges := {}
|
|
818
|
+
@topics := []
|
|
819
|
+
@acceptable := null
|
|
820
|
+
|
|
821
|
+
render
|
|
822
|
+
.brochure
|
|
823
|
+
Cover title: title, poweredBy: poweredBy, firstName: firstName
|
|
824
|
+
|
|
825
|
+
Summary
|
|
826
|
+
firstName: firstName, lastName: lastName, age: age, gender: gender
|
|
827
|
+
lab: lab, history: history, ranges: ranges, acceptable: acceptable
|
|
828
|
+
|
|
829
|
+
for topic in topics
|
|
830
|
+
DetailPage topic: topic, history: history, ranges: ranges, acceptable: acceptable
|
|
831
|
+
</script>
|
|
832
|
+
|
|
833
|
+
<script type="text/rip" data-name="cover">
|
|
834
|
+
# Cover — Brochure cover page
|
|
835
|
+
|
|
836
|
+
export Cover = component
|
|
837
|
+
@title := ''
|
|
838
|
+
@poweredBy := ''
|
|
839
|
+
@firstName := ''
|
|
840
|
+
|
|
841
|
+
today ~=
|
|
842
|
+
d = new Date()
|
|
843
|
+
months = ['January','February','March','April','May','June','July','August','September','October','November','December']
|
|
844
|
+
day = d.getDate()
|
|
845
|
+
suffix = 'th'
|
|
846
|
+
suffix = 'st' if day is 1 or day is 21 or day is 31
|
|
847
|
+
suffix = 'nd' if day is 2 or day is 22
|
|
848
|
+
suffix = 'rd' if day is 3 or day is 23
|
|
849
|
+
"#{months[d.getMonth()]} #{day}#{suffix}, #{d.getFullYear()}"
|
|
850
|
+
|
|
851
|
+
greeting ~=
|
|
852
|
+
name = firstName or '(First Name)'
|
|
853
|
+
"#{name}, here are the results and personalized action plan from your recent screening."
|
|
854
|
+
|
|
855
|
+
render
|
|
856
|
+
.('brochure__page page-cover') style: "background-image: url(images/cover_bg.jpg)"
|
|
857
|
+
.cover__heading-fade
|
|
858
|
+
.cover__title title
|
|
859
|
+
|
|
860
|
+
.cover__body
|
|
861
|
+
.cover__copy
|
|
862
|
+
span greeting
|
|
863
|
+
br
|
|
864
|
+
br
|
|
865
|
+
span today
|
|
866
|
+
|
|
867
|
+
.cover__footer
|
|
868
|
+
img src: 'images/crossover.svg', alt: 'Crossover Health'
|
|
869
|
+
.cover__powered-by
|
|
870
|
+
div "Powered by"
|
|
871
|
+
div poweredBy
|
|
872
|
+
</script>
|
|
873
|
+
|
|
874
|
+
<script type="text/rip" data-name="summary">
|
|
875
|
+
# Summary - Medical Summary table (Page 1)
|
|
876
|
+
|
|
877
|
+
export Summary = component
|
|
878
|
+
@firstName := ''
|
|
879
|
+
@lastName := ''
|
|
880
|
+
@age := 0
|
|
881
|
+
@gender := 'Male'
|
|
882
|
+
@lab := ''
|
|
883
|
+
@history := []
|
|
884
|
+
@ranges := {}
|
|
885
|
+
@acceptable := null
|
|
886
|
+
|
|
887
|
+
patientName ~=
|
|
888
|
+
f = firstName or '(First Name)'
|
|
889
|
+
l = lastName or '(Last Name)'
|
|
890
|
+
"#{f} #{l}"
|
|
891
|
+
|
|
892
|
+
formattedDate ~=
|
|
893
|
+
d = new Date()
|
|
894
|
+
mo = String(d.getMonth() + 1).padStart(2, '0')
|
|
895
|
+
dy = String(d.getDate()).padStart(2, '0')
|
|
896
|
+
yr = d.getFullYear()
|
|
897
|
+
"#{mo}/#{dy}/#{yr}"
|
|
898
|
+
|
|
899
|
+
historyDate ~=
|
|
900
|
+
return '' unless history and history[0] and history[0].date
|
|
901
|
+
parts = history[0].date.split('-')
|
|
902
|
+
"#{parts[1]}/#{parts[0]}"
|
|
903
|
+
|
|
904
|
+
checkIcon := '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>'
|
|
905
|
+
warnIcon := '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>'
|
|
906
|
+
|
|
907
|
+
sections ~=
|
|
908
|
+
s = []
|
|
909
|
+
s.push { label: 'Heart Health', keys: ['triglycerides', 'hdlCholesterol', 'totalCholesterol', 'cholesterolHdlRatio'] }
|
|
910
|
+
s.push { label: 'Pancreas Health', keys: ['glucose', 'a1c'] }
|
|
911
|
+
if history and history[0] and history[0].waistCircumference?
|
|
912
|
+
s.push { label: 'Physical Measurements', keys: ['waistCircumference', 'bloodPressure'] }
|
|
913
|
+
s
|
|
914
|
+
|
|
915
|
+
rows ~=
|
|
916
|
+
result = []
|
|
917
|
+
for section in sections
|
|
918
|
+
for k in section.keys
|
|
919
|
+
ref = ranges[k]
|
|
920
|
+
continue unless ref
|
|
921
|
+
val = history[0]?[k]
|
|
922
|
+
continue unless val?
|
|
923
|
+
status = acceptable(k, val)
|
|
924
|
+
result.push
|
|
925
|
+
section: section.label
|
|
926
|
+
key: k
|
|
927
|
+
name: ref.name
|
|
928
|
+
desc: ref.desc
|
|
929
|
+
value: String(val)
|
|
930
|
+
rowClass: if status is 'acceptable' then 'row-acceptable' else 'row-unacceptable'
|
|
931
|
+
icon: if status is 'acceptable' then checkIcon else warnIcon
|
|
932
|
+
result
|
|
933
|
+
|
|
934
|
+
render
|
|
935
|
+
.('brochure__page page-summary')
|
|
936
|
+
.page-heading "Medical Summary"
|
|
937
|
+
|
|
938
|
+
.summary__intro
|
|
939
|
+
.summary__patient
|
|
940
|
+
.summary__patient-name patientName
|
|
941
|
+
.summary__patient-meta "#{age ? age : '(age)'} years old | #{gender}"
|
|
942
|
+
.summary__patient-lab
|
|
943
|
+
div
|
|
944
|
+
strong "Screening results from: "
|
|
945
|
+
span formattedDate
|
|
946
|
+
div
|
|
947
|
+
strong "Testing facility: "
|
|
948
|
+
span lab
|
|
949
|
+
|
|
950
|
+
.summary__copy "This report serves as an easy reference to review all of your testing results, including data from previous years. We encourage you to use this information in conjunction with an exam by your doctor, not as a replacement for one. We hope this summary will be a good starting point for conversations with your doctor about improving your overall health."
|
|
951
|
+
|
|
952
|
+
table.summary-table
|
|
953
|
+
thead
|
|
954
|
+
tr
|
|
955
|
+
th ""
|
|
956
|
+
th historyDate
|
|
957
|
+
tbody
|
|
958
|
+
for row in rows
|
|
959
|
+
tr
|
|
960
|
+
class: row.rowClass
|
|
961
|
+
td
|
|
962
|
+
.status-icon
|
|
963
|
+
innerHTML: row.icon
|
|
964
|
+
div
|
|
965
|
+
.name row.name
|
|
966
|
+
.desc row.desc
|
|
967
|
+
td.value row.value
|
|
968
|
+
</script>
|
|
969
|
+
|
|
970
|
+
<script type="text/rip" data-name="detail-page">
|
|
971
|
+
# DetailPage - Reusable health detail page with gradient header and gauges
|
|
972
|
+
|
|
973
|
+
export DetailPage = component
|
|
974
|
+
@topic := {}
|
|
975
|
+
@history := []
|
|
976
|
+
@ranges := {}
|
|
977
|
+
@acceptable := null
|
|
978
|
+
|
|
979
|
+
shouldShow ~=
|
|
980
|
+
return true unless topic.conditional
|
|
981
|
+
history and history[0] and history[0][topic.conditional]?
|
|
982
|
+
|
|
983
|
+
tipIcons :=
|
|
984
|
+
utensils: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M3 2v7c0 1.1.9 2 2 2h4c1.1 0 2-.9 2-2V2"/><line x1="7" y1="2" x2="7" y2="22"/><path d="M21 15V2a5 5 0 00-5 5v6h4"/><line x1="21" y1="12" x2="21" y2="22"/></svg>'
|
|
985
|
+
drop: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 2.69l5.66 5.66a8 8 0 11-11.31 0z"/></svg>'
|
|
986
|
+
walk: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="13" cy="4" r="2"/><path d="M7 21l3-4V11l-2-2-3 3"/><path d="M16 21l-2-6-3-1"/></svg>'
|
|
987
|
+
doctor: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 11V3M4.93 7.5l7.07 3.5 7.07-3.5"/><circle cx="12" cy="18" r="3"/><line x1="12" y1="15" x2="12" y2="11"/></svg>'
|
|
988
|
+
smile: '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>'
|
|
989
|
+
|
|
990
|
+
checkIcon := '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>'
|
|
991
|
+
warnIcon := '<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>'
|
|
992
|
+
|
|
993
|
+
statusOf: (key) ->
|
|
994
|
+
val = history[0]?[key]
|
|
995
|
+
return 'acceptable' unless val?
|
|
996
|
+
acceptable(key, val)
|
|
997
|
+
|
|
998
|
+
iconFor: (key) ->
|
|
999
|
+
if @statusOf(key) is 'acceptable' then checkIcon else warnIcon
|
|
1000
|
+
|
|
1001
|
+
render
|
|
1002
|
+
if shouldShow
|
|
1003
|
+
.('brochure__page page-detail')
|
|
1004
|
+
if topic.gradient
|
|
1005
|
+
.detail__header style: "background: #{topic.gradient}"
|
|
1006
|
+
.detail__header-image style: "background-image: url(#{topic.image}); width: #{topic.imageWidth}px; height: #{topic.imageHeight}px"
|
|
1007
|
+
.detail__header-text
|
|
1008
|
+
.detail__header-title topic.title
|
|
1009
|
+
.detail__header-copy topic.copy
|
|
1010
|
+
|
|
1011
|
+
if topic.isImagePage
|
|
1012
|
+
.detail__header style: "background-image: url(#{topic.image}); background-size: cover"
|
|
1013
|
+
|
|
1014
|
+
.detail__content
|
|
1015
|
+
for bio in topic.biomarkers
|
|
1016
|
+
.detail__item
|
|
1017
|
+
if ranges[bio.key]
|
|
1018
|
+
Gauge value: (history[0]?[bio.key] or 0), units: ranges[bio.key].units, range: ranges[bio.key].range
|
|
1019
|
+
|
|
1020
|
+
.detail__item-header class: @statusOf(bio.key)
|
|
1021
|
+
.status-icon
|
|
1022
|
+
innerHTML: @iconFor(bio.key)
|
|
1023
|
+
div
|
|
1024
|
+
.name ranges[bio.key].name
|
|
1025
|
+
.desc ranges[bio.key].desc
|
|
1026
|
+
|
|
1027
|
+
.detail__item-info
|
|
1028
|
+
p innerHTML: bio.text
|
|
1029
|
+
if bio.extra
|
|
1030
|
+
p bio.extra
|
|
1031
|
+
|
|
1032
|
+
.detail__maintenance
|
|
1033
|
+
.detail__maintenance-heading "How To Maintain"
|
|
1034
|
+
for tip in bio.tips
|
|
1035
|
+
.detail__maintenance-item
|
|
1036
|
+
.detail__maintenance-icon
|
|
1037
|
+
innerHTML: (tipIcons[tip.icon] or '')
|
|
1038
|
+
div tip.text
|
|
1039
|
+
</script>
|
|
1040
|
+
|
|
1041
|
+
<script type="text/rip" data-name="gauge">
|
|
1042
|
+
# Gauge — SVG semi-circular gauge with rotating needle
|
|
1043
|
+
|
|
1044
|
+
export Gauge = component
|
|
1045
|
+
@value := 0
|
|
1046
|
+
@units := ''
|
|
1047
|
+
@range := []
|
|
1048
|
+
|
|
1049
|
+
gaugeType ~=
|
|
1050
|
+
switch range[0]
|
|
1051
|
+
when '<', '<=' then 'lessThan'
|
|
1052
|
+
when '>', '>=' then 'greaterThan'
|
|
1053
|
+
when '<>' then 'inBetween'
|
|
1054
|
+
else null
|
|
1055
|
+
|
|
1056
|
+
needleRotation ~=
|
|
1057
|
+
max = 75
|
|
1058
|
+
fraction = 0
|
|
1059
|
+
|
|
1060
|
+
if typeof range[1] is 'string' and String(range[1]).includes('/')
|
|
1061
|
+
bp = String(range[1]).split('/')
|
|
1062
|
+
bv = String(value).split('/')
|
|
1063
|
+
sf = Number(bv[0]) / (Number(bp[0]) * 2)
|
|
1064
|
+
df = Number(bv[1]) / (Number(bp[1]) * 2)
|
|
1065
|
+
fraction = (sf + df) / 2
|
|
1066
|
+
else
|
|
1067
|
+
switch gaugeType
|
|
1068
|
+
when 'lessThan', 'greaterThan'
|
|
1069
|
+
fraction = Number(value) / (Number(range[1]) * 2)
|
|
1070
|
+
when 'inBetween'
|
|
1071
|
+
fraction = Number(value) / (Number(range[1]) + Number(range[2]))
|
|
1072
|
+
|
|
1073
|
+
fraction = 1 if fraction >= 1
|
|
1074
|
+
fraction = 0 if fraction <= 0
|
|
1075
|
+
fraction * (max * 2) - max
|
|
1076
|
+
|
|
1077
|
+
render
|
|
1078
|
+
.('gauge', gaugeType is 'lessThan' and 'gauge--flip')
|
|
1079
|
+
if gaugeType is 'lessThan' or gaugeType is 'greaterThan'
|
|
1080
|
+
svg width: '170', height: '85', viewBox: '0 0 170 85'
|
|
1081
|
+
g stroke: 'none', fill: 'none'
|
|
1082
|
+
g transform: 'translate(-31, -36)'
|
|
1083
|
+
g transform: 'translate(30, 36)'
|
|
1084
|
+
path d: 'M86.09,0 C132.98,0 171,38.06 171,85.01 L156.85,85.01 C156.85,45.89 125.17,14.17 86.09,14.17 L86.09,0 Z', fill: '#2EAB27'
|
|
1085
|
+
path d: 'M1.18,0 C48.07,0 86.09,38.06 86.09,85.01 L71.94,85.01 C71.94,45.89 40.26,14.17 1.18,14.17 L1.18,0 Z', fill: '#FB2E47', transform: 'translate(43.63, 42.51) scale(-1, 1) translate(-43.63, -42.51)'
|
|
1086
|
+
g transform: 'translate(20.23, 19.25)'
|
|
1087
|
+
path d: 'M0.77,65.75 C0.77,29.85 30.1,0.75 66.27,0.75 C102.45,0.75 131.77,29.85 131.77,65.75 L0.77,65.75 Z', fill: '#2EAB27'
|
|
1088
|
+
path d: 'M8.77,65.75 C8.77,34.27 34.52,8.75 66.27,8.75 C98.03,8.75 123.77,34.27 123.77,65.75 L8.77,65.75 Z', fill: '#20781C'
|
|
1089
|
+
else
|
|
1090
|
+
svg width: '170', height: '85', viewBox: '0 0 170 85'
|
|
1091
|
+
g stroke: 'none', fill: 'none'
|
|
1092
|
+
g transform: 'translate(-31, -36)'
|
|
1093
|
+
g transform: 'translate(30, 0)'
|
|
1094
|
+
path d: 'M43.6,18.43 C90.49,18.43 128.51,56.49 128.51,103.44 L114.36,103.44 C114.36,64.32 82.68,32.6 43.6,32.6 L43.6,18.43 Z', fill: '#2EAB27', transform: 'translate(86.05, 60.94) rotate(-45) translate(-86.05, -60.94)'
|
|
1095
|
+
path d: 'M15.29,121 L1.13,121 C1.11,99.24 9.39,77.49 25.98,60.9 L36,70.92 C22.17,84.74 15.27,102.87 15.29,121 Z', fill: '#FB2E47'
|
|
1096
|
+
path d: 'M170.96,121 L156.8,121 C156.79,102.9 149.89,84.82 136.09,71.02 L146.11,61 C162.68,77.56 170.96,99.28 170.96,121 Z', fill: '#FB2E47'
|
|
1097
|
+
g transform: 'translate(20.23, 55.25)'
|
|
1098
|
+
path d: 'M0.77,65.75 C0.77,29.85 30.1,0.75 66.27,0.75 C102.45,0.75 131.77,29.85 131.77,65.75 L0.77,65.75 Z', fill: '#2EAB27'
|
|
1099
|
+
path d: 'M8.77,65.75 C8.77,34.27 34.52,8.75 66.27,8.75 C98.03,8.75 123.77,34.27 123.77,65.75 L8.77,65.75 Z', fill: '#20781C'
|
|
1100
|
+
|
|
1101
|
+
.gauge__needle style: "transform: rotate(#{needleRotation}deg)"
|
|
1102
|
+
.gauge__arrow
|
|
1103
|
+
.gauge__arrow-border
|
|
1104
|
+
|
|
1105
|
+
.gauge__value "#{value}"
|
|
1106
|
+
.gauge__units units
|
|
1107
|
+
</script>
|
|
1108
|
+
|
|
1109
|
+
<!-- ===== Boot ===== -->
|
|
1110
|
+
|
|
1111
|
+
<script type="text/rip">
|
|
1112
|
+
{ launch } = importRip! '/rip/ui.rip'
|
|
1113
|
+
launch hash: true
|
|
1114
|
+
</script>
|
|
1115
|
+
|
|
1116
|
+
</body>
|
|
1117
|
+
</html>
|