Patient Summary Viewer
44 |50 | 58 | 61 |
├── .npmrc ├── src ├── lib │ ├── HealthLinkOverviewItem.svelte │ ├── types.ts │ ├── util.ts │ ├── app.d.ts │ ├── issuer.private.jwks.json │ ├── config.ts │ ├── HealthLinkOverview.svelte │ ├── managementClient.ts │ ├── AddFile.svelte │ └── HealthLink.svelte ├── routes │ ├── +layout.ts │ ├── +page.svelte │ ├── view │ │ └── [id] │ │ │ └── +page.svelte │ ├── create │ │ └── +page.svelte │ ├── home │ │ └── +page.svelte │ └── +layout.svelte └── app.html ├── static ├── favicon.ico ├── favicon.png ├── img │ ├── menu.png │ ├── favicon-SMART.ico │ ├── favicon-SMART.png │ ├── waverifylogo.png │ ├── doh_logo_doh-black.png │ ├── waverifypluslogo.png │ ├── waverifypluslogobold.png │ └── smart-logo.svg ├── ips │ ├── assets │ │ ├── js │ │ │ ├── config.js │ │ │ ├── retreiveIPS.js │ │ │ └── renderIPS.js │ │ ├── html │ │ │ ├── footer.html │ │ │ └── header.html │ │ ├── html-waverify │ │ │ ├── footer.html │ │ │ └── header.html │ │ └── css │ │ │ └── custom.css │ ├── templates │ │ ├── Text.html │ │ ├── Composition.html │ │ ├── Patient.html │ │ ├── Immunizations.html │ │ ├── Problems.html │ │ ├── Allergies.html │ │ ├── Checks.html │ │ ├── AdvanceDirectives.html │ │ ├── Observations.html │ │ └── Medications.html │ ├── templates-waverify │ │ ├── Text.html │ │ ├── Patient.html │ │ ├── Composition.html │ │ ├── Immunizations.html │ │ ├── Problems.html │ │ ├── Allergies.html │ │ ├── Checks.html │ │ ├── AdvanceDirectives.html │ │ ├── Observations.html │ │ └── Medications.html │ └── index.html ├── color guide.html └── banner.js ├── default.env ├── fix-popper.sh ├── .gitignore ├── docker-compose.yaml ├── .prettierignore ├── vite.config.ts ├── .prettierrc ├── Dockerfile ├── tsconfig.json ├── svelte.config.js ├── package.json ├── .github └── workflows │ └── deploy.yml └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /src/lib/HealthLinkOverviewItem.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = false; 2 | export const ssr = false; 3 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/favicon.ico -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/favicon.png -------------------------------------------------------------------------------- /default.env: -------------------------------------------------------------------------------- 1 | # Port to expose to internet; use in SERVER_NAME (portal.env) 2 | # EXTERNAL_PORT= -------------------------------------------------------------------------------- /static/img/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/img/menu.png -------------------------------------------------------------------------------- /static/img/favicon-SMART.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/img/favicon-SMART.ico -------------------------------------------------------------------------------- /static/img/favicon-SMART.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/img/favicon-SMART.png -------------------------------------------------------------------------------- /static/img/waverifylogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/img/waverifylogo.png -------------------------------------------------------------------------------- /fix-popper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sed -i '/2\.11\.6/a \ \ "type": "module",' node_modules/@popperjs/core/package.json 3 | -------------------------------------------------------------------------------- /static/img/doh_logo_doh-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/img/doh_logo_doh-black.png -------------------------------------------------------------------------------- /static/img/waverifypluslogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/img/waverifypluslogo.png -------------------------------------------------------------------------------- /static/img/waverifypluslogobold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shl-ips/main/static/img/waverifypluslogobold.png -------------------------------------------------------------------------------- /static/ips/assets/js/config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | html_dir: "/ips/assets/html-waverify/", 3 | template_dir: "/ips/templates-waverify/" 4 | } 5 | 6 | export default config; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3.4" 3 | services: 4 | shlips: 5 | build: ./ 6 | ports: 7 | - "127.0.0.1:${EXTERNAL_PORT:-3000}:3000" 8 | env_file: 9 | - .env -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig(({ mode }) => ({ 5 | plugins: [sveltekit()], 6 | server: { 7 | host: true, 8 | port: 3000 9 | } 10 | })); 11 | -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type Bundle = unknown; 2 | export interface SHCRetrieveEvent { 3 | shc: SHCFile; 4 | label?: string; 5 | content: Bundle; 6 | exp?: number; 7 | } 8 | 9 | export interface SHCFile { 10 | verifiableCredential: string[]; 11 | } 12 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "pluginSearchDirs": ["."], 8 | "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/util.ts: -------------------------------------------------------------------------------- 1 | import * as jose from 'jose'; 2 | 3 | export const base64url = jose.base64url; 4 | 5 | export function randomStringWithEntropy(entropy = 32): string { 6 | const b = new Uint8Array(entropy); 7 | crypto.getRandomValues(b); 8 | return base64url.encode(b); 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface Platform {} 9 | } 10 | } 11 | 12 | export {}; 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 as build-deps 2 | 3 | EXPOSE 3000 4 | 5 | # ENV DIRPATH /opt/app 6 | ENV NODE_ENV production 7 | 8 | WORKDIR /opt/app 9 | 10 | COPY package*.json ./ 11 | RUN npm clean-install --include=dev 12 | 13 | COPY ./fix-popper.sh ./ 14 | RUN ./fix-popper.sh 15 | 16 | COPY . . 17 | RUN npm run build 18 | 19 | RUN cp build/404.html build/index.html 20 | 21 | CMD ["npm", "run", "start"] -------------------------------------------------------------------------------- /src/lib/issuer.private.jwks.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | { 4 | "kty": "EC", 5 | "kid": "3Kfdg-XwP-7gXyywtUfUADwBumDOPKMQx-iELL11W9s", 6 | "use": "sig", 7 | "alg": "ES256", 8 | "crv": "P-256", 9 | "x": "11XvRWy1I2S0EyJlyf_bWfw_TQ5CJJNLw78bHXNxcgw", 10 | "y": "eZXwxvO1hvCY0KucrPfKo7yAyMT6Ajc3N7OkAB6VYy8", 11 | "d": "FvOOk6hMixJ2o9zt4PCfan_UW7i4aOEnzj76ZaCI9Og" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | %sveltekit.head% 9 | 10 | 11 |
10 | Name: {{name[0].given}}, {{name[0].family}}
11 |
12 | Birth Date: {{birthDate}}
13 |
11 | Summary Date: {{date}}
12 |
13 |
11 | Summary Date: {{date}}
12 |
13 |
11 | Birth Date: {{birthDate}}
12 |
13 | Name: {{name[0].given}}, {{name[0].family}}
14 |
| Section | 17 |Entries | 18 |Narrative | 19 | 20 | {{each(options.data)}} 21 |
|---|---|---|
| {{@this.display}} | 23 |{{@this.entries}} | 24 |{{@this.narrative}} | 25 |
| Section | 17 |Entries | 18 |Narrative | 19 | 20 | {{each(options.data)}} 21 |
|---|---|---|
| {{@this.display}} | 23 |{{@this.entries}} | 24 |{{@this.narrative}} | 25 |
9 |
22 |
8 |
21 | Welcome to a demonstration of WA State’s ability to allow individuals in WA State to share their health data, including state immunization records. 35 |
36 | 37 |The WA Verify+ system builds on the WA Verify vaccination verification system to allow people to access and share their own health data, stored for them by the Washington State Department of Health, including immunizations, advance directives, and other data specified by the International Patient Summary. This data may be helpful to those traveling away from home, to parents and caregivers, and to anyone who wants to be able to see their own records, or securely share their data with healthcare providers or others of their choosing. 38 |
39 | 40 |WA Verify+ uses the secure, patient-controlled SMART Health Link standard. If you would like to share your records, you may use either the electronic or a printed version of the QR Code you’ll get from the system. We recommend waiting 3-7 days for any new immunization to show up in the State system, and therefore be available in this International Patient Summary.
41 | 42 |You can start by entering a name, date of birth and cell phone number to generate a QR Code to access your records. You will receive a code on your cell phone which you will need to enter each time you sign in.
43 | 44 || Name | 12 |Date | 13 |Value | 14 |Category | 15 |
|---|---|---|---|
| 21 | {{if(options.observations[@index].code && options.observations[@index].code.coding)}} 22 | {{@this.code.coding[0].display}} 23 | ({{@this.code.coding[0].code}}) 24 | {{/if}} 25 | {{if(options.observations[@index].code.text)}} 26 | [Uncoded text shown]: {{@this.code.text}} 27 | {{/if}} 28 | | 29 |30 | {{if(options.observations[@index].effectiveDateTime)}} 31 | {{@this.effectiveDateTime}} 32 | {{/if}} 33 | | 34 |35 | {{if(options.observations[@index].valueCodeableConcept)}} 36 | {{@this.valueCodeableConcept.coding[0].display}} 37 | {{/if}} 38 | {{if(options.observations[@index].valueQuantity)}} 39 | {{@this.valueQuantity.value}} 40 | {{@this.valueQuantity.unit}} 41 | {{/if}} 42 | {{if(options.observations[@index].valueString)}} 43 | {{@this.valueString}} 44 | {{/if}} 45 | | 46 | {{if(options.observations[@index].category && options.observations[@index].category[0] && options.observations[@index].category[0].coding && options.observations[@index].category[0].coding[0])}} 47 |48 | {{@this.category[0].coding[0].code}} 49 | | 50 | {{/if}} 51 |
| Name | 12 |Date | 13 |Value | 14 |Category | 15 |
|---|---|---|---|
| 21 | {{if(options.observations[@index].code && options.observations[@index].code.coding)}} 22 | {{@this.code.coding[0].display}} 23 | ({{@this.code.coding[0].code}}) 24 | {{/if}} 25 | {{if(options.observations[@index].code.text)}} 26 | [Uncoded text shown]: {{@this.code.text}} 27 | {{/if}} 28 | | 29 |30 | {{if(options.observations[@index].effectiveDateTime)}} 31 | {{@this.effectiveDateTime}} 32 | {{/if}} 33 | | 34 |35 | {{if(options.observations[@index].valueCodeableConcept)}} 36 | {{@this.valueCodeableConcept.coding[0].display}} 37 | {{/if}} 38 | {{if(options.observations[@index].valueQuantity)}} 39 | {{@this.valueQuantity.value}} 40 | {{@this.valueQuantity.unit}} 41 | {{/if}} 42 | {{if(options.observations[@index].valueString)}} 43 | {{@this.valueString}} 44 | {{/if}} 45 | | 46 | {{if(options.observations[@index].category && options.observations[@index].category[0] && options.observations[@index].category[0].coding && options.observations[@index].category[0].coding[0])}} 47 |48 | {{@this.category[0].coding[0].code}} 49 | | 50 | {{/if}} 51 |
| Composition | ||||
|---|---|---|---|---|
| Ingredient | 33 |Strength Numerator Qty | 34 |Unit | 35 |Strength Denominator Qty | 36 |Strength Denominator Unit | 37 |
| {{@this.itemCodeableConcept.coding[0].display}} | 41 |{{@this.strength.numerator.value}} | 42 |{{@this.strength.numerator.unit}} | 43 |{{@this.strength.denominator.value}} | 44 |{{@this.strength.denominator.unit}} | 45 |
| Dosage | ||||
|---|---|---|---|---|
| Route | 55 |Qty | 56 |Unit | 57 |Freq. Qty | 58 |Freq. Period | 59 |
| {{@this.statement.dosage[0].route.coding[0].display}} | 63 |{{@this.statement.dosage[0].doseAndRate[0].doseQuantity.value}} | 64 |{{@this.statement.dosage[0].doseAndRate[0].doseQuantity.unit}} | 65 | {{if(options.medications[@index].statement.dosage[0].timing && options.medications[@index].statement.dosage[0].timing.repeat)}} 66 |{{@this.statement.dosage[0].timing.repeat.count}} | 67 |{{@this.statement.dosage[0].timing.repeat.periodUnit}} | 68 | {{/if}} 69 |
| Composition | ||||
|---|---|---|---|---|
| Ingredient | 41 |Strength Numerator Qty | 42 |Unit | 43 |Strength Denominator Qty | 44 |Strength Denominator Unit | 45 |
| {{@this.itemCodeableConcept.coding[0].display}} | 49 |{{@this.strength.numerator.value}} | 50 |{{@this.strength.numerator.unit}} | 51 |{{@this.strength.denominator.value}} | 52 |{{@this.strength.denominator.unit}} | 53 |
| Dosage | ||||
|---|---|---|---|---|
| Route | 63 |Qty | 64 |Unit | 65 |Freq. Qty | 66 |Freq. Period | 67 |
| {{@this.statement.dosage[0].route.coding[0].display}} | 71 |{{@this.statement.dosage[0].doseAndRate[0].doseQuantity.value}} | 72 |{{@this.statement.dosage[0].doseAndRate[0].doseQuantity.unit}} | 73 | {{if(options.medications[@index].statement.dosage[0].timing && options.medications[@index].statement.dosage[0].timing.repeat)}} 74 |{{@this.statement.dosage[0].timing.repeat.count}} | 75 |{{@this.statement.dosage[0].timing.repeat.periodUnit}} | 76 | {{/if}} 77 |
87 |
88 |
89 |