-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add viewer demo environment variable * Move viewer resources to lib folder * Refactor shared navigation components between layouts * Implement viewer layout and logic in svelte components * Limit global accordion body styling in IPSContent component to specific class * Limit IPSContent global table styling to ips-section class * Fix viewer loading animation * Increase viewer section max height * Re-add global modifier to loader styling
- Loading branch information
1 parent
504cc6e
commit 42c3338
Showing
22 changed files
with
3,424 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<script lang="ts"> | ||
import { Row, Col, Image } from 'sveltestrap'; | ||
export let title = 'International Patient Summary'; | ||
</script> | ||
|
||
<Row | ||
style=" | ||
padding:0px 12px; | ||
margin-left: 0px; | ||
margin-right: 0px; | ||
margin-bottom: 20px; | ||
border-bottom: 1px solid rgb(204, 204, 204);" | ||
class="d-flex justify-content-between align-items-center" | ||
> | ||
<Col style="max-width: 200px"> | ||
<Image | ||
alt="WA Verify Logo" | ||
width="200" | ||
src="/img/waverifypluslogo.png" | ||
style="align-self: center" | ||
/> | ||
</Col> | ||
<Col> | ||
<div | ||
style=" | ||
vertical-align: middle; | ||
font-size: 18px; | ||
display: inline-block; | ||
padding-left: 17px; | ||
font-family: Verdana, sans-serif; | ||
color: rgb(34, 72, 156);" | ||
> | ||
{title} | ||
</div> | ||
</Col> | ||
</Row> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
<script lang='ts'> | ||
import { NavItem, NavLink } from 'sveltestrap'; | ||
</script> | ||
|
||
<NavItem> | ||
<NavLink href="/home">Home</NavLink> | ||
</NavItem> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<script lang="ts"> | ||
import { Dropdown, DropdownItem, DropdownMenu, DropdownToggle, Icon } from 'sveltestrap'; | ||
// import { locale } from 'svelte-i18n'; // TODO | ||
import { getContext } from 'svelte'; | ||
import type { Writable, Readable } from 'svelte/store'; | ||
import type { Language } from '$lib/types'; | ||
let locale: Writable<string> = getContext('locale'); | ||
let locales: Readable<Record<string, Language>> = getContext('locales'); | ||
</script> | ||
|
||
<Dropdown nav inNavbar size="sm" direction="down"> | ||
<DropdownToggle color="primary" nav caret> | ||
<Icon name="globe2" /> | ||
{$locales[$locale].lang} | ||
</DropdownToggle> | ||
<DropdownMenu end style="height: 500px; overflow:auto"> | ||
{#if $locales} | ||
{#each Object.values($locales) as loc} | ||
<DropdownItem | ||
style="text-overflow: ellipsis; white-space: nowrap; overflow: hidden;" | ||
on:click={() => { | ||
$locale = loc.code; | ||
}} | ||
> | ||
{`${loc.lang_en}${loc.lang_en !== loc.lang ? ' - ' + loc.lang : ''}`} | ||
</DropdownItem> | ||
{/each} | ||
{/if} | ||
</DropdownMenu> | ||
</Dropdown> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
<!-- Submit data view w/ input box, "try a sample" button, link to sample list, clear, and submit buttons --> | ||
<!-- Simple checks view --> | ||
<!-- Link to validation --> | ||
<script lang="ts"> | ||
import { | ||
Button, | ||
Col, | ||
FormGroup, | ||
Icon, | ||
Input, | ||
Label, | ||
Row | ||
} from "sveltestrap"; | ||
import { onMount } from "svelte"; | ||
import type { Bundle } from "fhir/r4"; | ||
import IpsContent from "./IPSContent.svelte"; | ||
export let content: Bundle | undefined; | ||
export let mode: string; | ||
let demoContent: string; | ||
onMount (() => { | ||
if (!content) { | ||
loadSample().then(submit); | ||
} | ||
}); | ||
$: { | ||
if (!content) { | ||
demoContent = ""; | ||
} else { | ||
demoContent = JSON.stringify(content, null, 2); | ||
} | ||
} | ||
let textInput: string; | ||
$: textInput = demoContent; | ||
let error: string[] = []; | ||
let valid: boolean = true; | ||
let invalid: boolean = false; | ||
$: { | ||
if (textInput) { | ||
try { | ||
let result = checks(JSON.parse(textInput)); | ||
if (result.errors) { | ||
result.errors.forEach(element => { | ||
setInputError(element); | ||
}); | ||
} else { | ||
clearInputErrors(); | ||
} | ||
} catch (e: any) { | ||
setInputError(e.message); | ||
} | ||
} | ||
} | ||
function setInputError(message: string) { | ||
valid = false; | ||
invalid = true; | ||
error.push(message); | ||
} | ||
function clearInputErrors() { | ||
valid = true; | ||
invalid = false; | ||
error = []; | ||
} | ||
async function loadSample() { | ||
let sample = await fetch('/samples/sample.json').then(function(response) { | ||
if (!response.ok) { | ||
// make the promise be rejected if we didn't get a 2xx response | ||
throw new Error("Unable to fetch IPS", {cause: response}); | ||
} else { | ||
return response; | ||
} | ||
}).then(function(response) { | ||
if (!response.ok) { | ||
// make the promise be rejected if we didn't get a 2xx response | ||
throw new Error("Unable to fetch IPS", {cause: response}); | ||
} else { | ||
return response.text(); | ||
} | ||
}).then((text) => { | ||
return JSON.stringify(JSON.parse(text), null, 2); | ||
}).catch(function (e) { | ||
console.log("error", e); | ||
}); | ||
if (sample) { | ||
textInput = sample; | ||
} | ||
} | ||
function clear() { | ||
textInput = ""; | ||
} | ||
let submitted = false; | ||
function submit() { | ||
try { | ||
content = JSON.parse(textInput); | ||
clearInputErrors(); | ||
submitted = true; | ||
setTimeout(() => submitted = false, 2000); | ||
} catch (e: any) { | ||
setInputError(e.message); | ||
} | ||
} | ||
interface ValidationResult { | ||
display?: string, | ||
entries?: number, | ||
entriesColor?: string, | ||
narrative?: string, | ||
narrativeColor?: string | ||
}; | ||
function checks(ips: Bundle) { | ||
let composition = ips.entry?.[0]; | ||
let data = { | ||
data: [] as ValidationResult[], | ||
errors: [] as string[] | ||
}; | ||
if (composition?.resource?.resourceType === "Composition" && composition.resource.section) { | ||
let sections = { | ||
allergies: false, | ||
medications: false, | ||
problems: false | ||
}; | ||
for (let i = 0; i < composition.resource.section.length; i++) { | ||
let section = composition.resource.section[i] | ||
let newData = {} as ValidationResult; | ||
newData.display = section.title; | ||
if (section.code?.coding?.[0]?.code == "48765-2") sections.allergies = true; | ||
if (section.code?.coding?.[0]?.code == "10160-0") sections.medications = true; | ||
if (section.code?.coding?.[0]?.code == "11450-4") sections.problems = true; | ||
if (section.entry) { | ||
newData.entries = section.entry.length; | ||
newData.entriesColor = "green"; | ||
} else { | ||
newData.entries = 0; | ||
newData.entriesColor = "red"; | ||
} | ||
if (section.text && section.text.div) { | ||
newData.narrative = "✓" | ||
newData.narrativeColor = "green"; | ||
} else { | ||
newData.narrative = "✗" | ||
newData.narrativeColor = "red"; | ||
} | ||
data.data.push(newData); | ||
} | ||
if (!sections.allergies) data.errors.push("Missing required allergies section"); | ||
if (!sections.medications) data.errors.push("Missing required medications section"); | ||
if (!sections.problems) data.errors.push("Missing required problems section"); | ||
} | ||
return data; | ||
} | ||
</script> | ||
<Row class="mx-1"> | ||
<h3>Submit Data</h3> | ||
<p>This is for test data only. <span class="text-danger"><strong>Please do not submit PHI.</strong></span></p> | ||
<FormGroup> | ||
<Label>Paste your IPS JSON here:</Label> | ||
<Input rows={8} type="textarea" bind:value={textInput} {valid} {invalid} feedback={error} class="pr-10"/> | ||
</FormGroup> | ||
</Row> | ||
<Row class="mx-3" cols={{ sm: 2, xs: 1 }}> | ||
<Col class="d-flex justify-content-start align-items-center"> | ||
<Button class="m-1" color="danger" on:click={clear}>Clear</Button> | ||
<Button class="m-1" color="primary" on:click={submit}>Submit</Button> | ||
{#if submitted} | ||
<Icon name="check" class="text-success fs-5"/> | ||
{/if} | ||
</Col> | ||
<Col class="d-flex justify-content-end align-items-center"> | ||
<Col class="m-1 justify-content-end align-items-center"> | ||
<Button color="success" on:click={loadSample} style="width:max-content">Try a Sample</Button> | ||
</Col> | ||
<Col class="d-flex flex-fill justify-content-start align-items-center"> | ||
<a href="https://github.com/jddamore/IPSviewer/tree/main/samples" class="m-1" target="_blank" rel="noreferrer">Repository of IPS Samples</a> | ||
</Col> | ||
</Col> | ||
</Row> | ||
{#if content} | ||
<Row> | ||
<IpsContent content={content} mode={mode} /> | ||
</Row> | ||
{/if} | ||
|
||
<style> | ||
:global(textarea.form-control.is-valid, textarea.form-control.is-invalid) { | ||
background-position: top calc(.375em + .1875rem) right 1.5rem !important; | ||
} | ||
</style> |
Oops, something went wrong.