├── .npmrc ├── src ├── routes │ ├── +layout.ts │ └── +page.svelte ├── types.ts ├── util.ts ├── app.d.ts ├── config.ts ├── app.html ├── issuer.private.jwks.json ├── HealthLink.svelte ├── managementClient.ts └── AddFile.svelte ├── static ├── favicon.png └── ips │ ├── assets │ └── css │ │ └── custom.css │ ├── templates │ ├── Text.html │ ├── Composition.html │ ├── Patient.html │ ├── Immunizations.html │ ├── Problems.html │ ├── Allergies.html │ ├── Observations.html │ └── Medications.html │ └── index.html ├── vite.config.ts ├── .gitignore ├── .prettierignore ├── .prettierrc ├── tsconfig.json ├── svelte.config.js ├── package.json ├── .github └── workflows │ └── deploy.yml └── README.md /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | export const prerender = true; -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jddamore/shlips/main/static/favicon.png -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()] 6 | }); 7 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type Bundle = unknown; 2 | export interface SHCRetrieveEvent { 3 | shc: SHCFile; 4 | patient: string; 5 | content: Bundle; 6 | } 7 | 8 | export interface SHCFile { 9 | "verifiableCredential": string[] 10 | } 11 | -------------------------------------------------------------------------------- /.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/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/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 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import { browser, dev} from '$app/environment'; 2 | 3 | export const API_BASE = 'https://api.vaxx.link/api'; 4 | export const VIEWER_BASE = (browser && !dev) ? new URL("ips#", window.location.href).toString(): 'http://localhost:5173/ips/index.html#' ; 5 | export const EXAMPLE_IPS = 'https://ips.health/fhir/Patient/98549f1a-e0d5-4454-849c-f5b97d3ed299/$summary' -------------------------------------------------------------------------------- /static/ips/assets/css/custom.css: -------------------------------------------------------------------------------- 1 | .card { 2 | margin-bottom: 15px; 3 | } 4 | 5 | .icon-action { 6 | margin-top: 5px; 7 | float: right; 8 | font-size: 80%; 9 | } 10 | 11 | .list-group-item .title { 12 | margin-top: 5px; 13 | margin-bottom: 12px; 14 | font-weight: 600; 15 | } 16 | 17 | .textBody table { 18 | table-layout: fixed; 19 | width: 100%; 20 | 21 | } -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
11 | Summary Date: {{date}}
12 |
13 |
14 |
11 | Birth Date: {{birthDate}}
12 |
13 | Name: {{name[0].given}}, {{name[0].family}}
14 |
| 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.code}} 41 | {{/if}} 42 | 43 | | 44 | {{if(options.observations[@index].category && options.observations[@index].category[0] && options.observations[@index].category[0].coding && options.observations[@index].category[0].coding[0])}} 45 |46 | {{@this.category[0].coding[0].code}} 47 | | 48 | {{/if}} 49 |
| Composition | ||||
|---|---|---|---|---|
| Ingredient | 35 |Strength Numerator Qty | 36 |Unit | 37 |Strength Denominator Qty | 38 |Strength Denominator Unit | 39 |
| {{@this.itemCodeableConcept.coding[0].display}} | 43 |{{@this.strength.numerator.value}} | 44 |{{@this.strength.numerator.unit}} | 45 |{{@this.strength.denominator.value}} | 46 |{{@this.strength.denominator.unit}} | 47 |
| Posología | ||||
|---|---|---|---|---|
| Route | 57 |Qty | 58 |Unit | 59 |Freq. Qty | 60 |Freq. Period | 61 |
| {{@this.statement.dosage[0].route.coding[0].display}} | 65 |{{@this.statement.dosage[0].doseAndRate[0].doseQuantity.value}} | 66 |{{@this.statement.dosage[0].doseAndRate[0].doseQuantity.unit}} | 67 | {{if(options.medications[@index].statement.dosage[0].timing && options.medications[@index].statement.dosage[0].timing.repeat)}} 68 |{{@this.statement.dosage[0].timing.repeat.count}} | 69 |{{@this.statement.dosage[0].timing.repeat.periodUnit}} | 70 | {{/if}} 71 |