From 338827caef584b8697a59efc73200066fe2d5e39 Mon Sep 17 00:00:00 2001 From: Roy Date: Mon, 23 Oct 2023 23:50:12 -0500 Subject: [PATCH 1/9] - JS files converted to typescript - Measurement Vue files converted to JS --- .gitignore | 1 - index.html | 2 +- package-lock.json | 195 +++++++++++++++++- package.json | 11 +- src/classes/{insight.js => insight.ts} | 147 +++++++------ src/classes/logEntry.js | 89 -------- src/classes/logEntry.ts | 76 +++++++ src/components/Dashboard/Dashboard.vue | 4 +- src/components/Interface/EditFields.vue | 10 +- src/components/Interface/EditableContent.vue | 63 ------ .../Interface/HeaderTitleCenter.vue | 41 ++-- src/components/Interface/HeaderTitleLeft.vue | 29 ++- src/components/Layout.vue | 2 +- src/components/Measurements/Create.vue | 43 ++-- src/components/Measurements/Form.vue | 127 +++++------- src/components/Measurements/Update.vue | 76 ++++--- src/components/People/PersonListItem.vue | 14 +- src/helpers/{encrypto.js => encrypto.ts} | 63 +++--- src/helpers/{storage.js => storage.ts} | 94 ++++----- src/index.d.ts | 8 - src/{main.js => main.ts} | 0 src/providers/{indexeddb.js => indexeddb.ts} | 5 +- .../{syncedstore.js => syncedstore.ts} | 21 +- src/providers/{webrtc.js => webrtc.ts} | 19 +- src/router/{index.js => index.ts} | 1 + ...js => chartjs-plugin-verticalMouseLine.ts} | 49 +++-- src/scripts/chartjs.js | 9 - src/scripts/chartjs.ts | 28 +++ src/store/measurements.js | 95 --------- src/store/measurements.ts | 85 ++++++++ src/store/notes.js | 76 ------- src/store/notes.ts | 79 +++++++ src/store/people.js | 71 ------- src/store/people.ts | 71 +++++++ src/store/{person.js => person.ts} | 33 +-- src/store/{record.js => record.ts} | 7 +- src/store/ui.js | 6 - src/store/ui.ts | 6 + src/store/vitals.js | 105 ---------- src/store/vitals.ts | 101 +++++++++ src/typedefs.js | 96 --------- src/types/index.d.ts | 76 +++++++ src/types/measurement.d.ts | 25 +++ tsconfig.json | 34 +++ vite.config.js => vite.config.ts | 1 - 45 files changed, 1185 insertions(+), 1009 deletions(-) rename src/classes/{insight.js => insight.ts} (61%) delete mode 100644 src/classes/logEntry.js create mode 100644 src/classes/logEntry.ts delete mode 100644 src/components/Interface/EditableContent.vue rename src/helpers/{encrypto.js => encrypto.ts} (64%) rename src/helpers/{storage.js => storage.ts} (53%) delete mode 100644 src/index.d.ts rename src/{main.js => main.ts} (100%) rename src/providers/{indexeddb.js => indexeddb.ts} (78%) rename src/providers/{syncedstore.js => syncedstore.ts} (67%) rename src/providers/{webrtc.js => webrtc.ts} (74%) rename src/router/{index.js => index.ts} (99%) rename src/scripts/{chartjs-plugin-verticalMouseLine.js => chartjs-plugin-verticalMouseLine.ts} (58%) delete mode 100644 src/scripts/chartjs.js create mode 100644 src/scripts/chartjs.ts delete mode 100644 src/store/measurements.js create mode 100644 src/store/measurements.ts delete mode 100644 src/store/notes.js create mode 100644 src/store/notes.ts delete mode 100644 src/store/people.js create mode 100644 src/store/people.ts rename src/store/{person.js => person.ts} (60%) rename src/store/{record.js => record.ts} (60%) delete mode 100644 src/store/ui.js create mode 100644 src/store/ui.ts delete mode 100644 src/store/vitals.js create mode 100644 src/store/vitals.ts create mode 100644 src/types/index.d.ts create mode 100644 src/types/measurement.d.ts create mode 100644 tsconfig.json rename vite.config.js => vite.config.ts (98%) diff --git a/.gitignore b/.gitignore index cfee706..b1a4580 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,3 @@ dev-dist *.njsproj *.sln *.sw? -jsconfig.json diff --git a/index.html b/index.html index 2e230e8..b9ed774 100644 --- a/index.html +++ b/index.html @@ -27,6 +27,6 @@
- + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0226a61..e3d5e0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,13 +29,17 @@ "yjs": "^13.6.8" }, "devDependencies": { + "@types/number-to-words": "^1.2.2", + "@types/pluralize": "^0.0.32", "@vitejs/plugin-vue": "^4.2.3", "@vue/cli-plugin-router": "~5.0.0", "autoprefixer": "^10.4.15", "postcss": "^8.4.29", "tailwindcss": "^3.3.3", + "typescript": "^5.2.2", "vite": "^4.4.5", - "vite-plugin-pwa": "^0.16.5" + "vite-plugin-pwa": "^0.16.5", + "vue-tsc": "^1.8.20" } }, "node_modules/@achrinza/node-ipc": { @@ -2644,6 +2648,12 @@ "integrity": "sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==", "dev": true }, + "node_modules/@types/number-to-words": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/number-to-words/-/number-to-words-1.2.2.tgz", + "integrity": "sha512-qNG5RJO5+37XRQ/O5VHTnZSNFfOixxsrRteMimQH2E0EmVzNOIvRjkePJjgI48qR7JwBBDPVe0KBF2k/F2Sj2Q==", + "dev": true + }, "node_modules/@types/parse-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.1.tgz", @@ -2651,6 +2661,12 @@ "dev": true, "peer": true }, + "node_modules/@types/pluralize": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.32.tgz", + "integrity": "sha512-exDkoRIkWJlbRDRmtYDbI3ZUE28HwBwHe5VKn4mvpvMW7qIRDHO6URItErBsBSX7J8/PrDLSOHCcbUMFXwA6CA==", + "dev": true + }, "node_modules/@types/qs": { "version": "6.9.9", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", @@ -2753,6 +2769,33 @@ "vue": "^3.2.25" } }, + "node_modules/@volar/language-core": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.10.4.tgz", + "integrity": "sha512-Na69qA6uwVIdA0rHuOc2W3pHtVQQO8hCNim7FOaKNpRJh0oAFnu5r9i7Oopo5C4cnELZkPNjTrbmpcCTiW+CMQ==", + "dev": true, + "dependencies": { + "@volar/source-map": "1.10.4" + } + }, + "node_modules/@volar/source-map": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.10.4.tgz", + "integrity": "sha512-RxZdUEL+pV8p+SMqnhVjzy5zpb1QRZTlcwSk4bdcBO7yOu4rtEWqDGahVCEj4CcXour+0yJUMrMczfSCpP9Uxg==", + "dev": true, + "dependencies": { + "muggle-string": "^0.3.1" + } + }, + "node_modules/@volar/typescript": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.10.4.tgz", + "integrity": "sha512-BCCUEBASBEMCrz7qmNSi2hBEWYsXD0doaktRKpmmhvb6XntM2sAWYu6gbyK/MluLDgluGLFiFRpWgobgzUqolg==", + "dev": true, + "dependencies": { + "@volar/language-core": "1.10.4" + } + }, "node_modules/@vue/cli-overlay": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-5.0.8.tgz", @@ -3072,6 +3115,54 @@ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz", "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==" }, + "node_modules/@vue/language-core": { + "version": "1.8.20", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.20.tgz", + "integrity": "sha512-vNJaqjCTSrWEr+erSq6Rq0CqDC8MOAwyxirxwK8esOxd+1LmAUJUTG2p7I84Mj1Izy5uHiHQAkRTVR2QxMBY+A==", + "dev": true, + "dependencies": { + "@volar/language-core": "~1.10.4", + "@volar/source-map": "~1.10.4", + "@vue/compiler-dom": "^3.3.0", + "@vue/shared": "^3.3.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "muggle-string": "^0.3.1", + "vue-template-compiler": "^2.7.14" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vue/language-core/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@vue/reactivity": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.6.tgz", @@ -3128,6 +3219,16 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.6.tgz", "integrity": "sha512-Xno5pEqg8SVhomD0kTSmfh30ZEmV/+jZtyh39q6QflrjdJCXah5lrnOLi9KB6a5k5aAHXMXjoMnxlzUkCNfWLQ==" }, + "node_modules/@vue/typescript": { + "version": "1.8.20", + "resolved": "https://registry.npmjs.org/@vue/typescript/-/typescript-1.8.20.tgz", + "integrity": "sha512-F0XX1wK71Fo9ewtzLSCSo5dfOuwKrSi/dR2AlI00iTJ4Bfk0wq1BNTRgnlvfx4kz/vQovaGXqwpIkif14W9KrA==", + "dev": true, + "dependencies": { + "@volar/typescript": "~1.10.4", + "@vue/language-core": "1.8.20" + } + }, "node_modules/@vue/vue-loader-v15": { "name": "vue-loader", "version": "15.11.1", @@ -4411,6 +4512,12 @@ "dev": true, "peer": true }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4911,6 +5018,12 @@ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -6364,7 +6477,6 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, - "peer": true, "bin": { "he": "bin/he" } @@ -8008,6 +8120,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/muggle-string": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", + "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", + "dev": true + }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", @@ -11116,6 +11234,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -11496,6 +11627,16 @@ "dev": true, "peer": true }, + "node_modules/vue-template-compiler": { + "version": "2.7.15", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.15.tgz", + "integrity": "sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, "node_modules/vue-template-es2015-compiler": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", @@ -11503,6 +11644,56 @@ "dev": true, "peer": true }, + "node_modules/vue-tsc": { + "version": "1.8.20", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.20.tgz", + "integrity": "sha512-bIADlyxJl+1ZWQQHAi47NZoi2iTiw/lBwQLL98wXROcQlUuGVtyroAIiqvto9pJotcyhtU0JbGvsHN6JN0fYfg==", + "dev": true, + "dependencies": { + "@vue/language-core": "1.8.20", + "@vue/typescript": "1.8.20", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/vue-tsc/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vue-tsc/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/vue-tsc/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index 267e1fd..17585c6 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "vite build", + "build": "vue-tsc && vite build", "preview": "vite preview", "generate-pwa-icon": "pwa-asset-generator public/healthrecord.svg public/assets/ -b \"rgb(229, 231, 235)\" -o false -c -i ./index.html -v /assets -m ./public/manifest.webmanifest", "generate-pwa-splash": "pwa-asset-generator public/healthrecord.svg public/assets/ -b \"rgb(229, 231, 235)\" -o false -p -i ./index.html -v /assets \"32%\" -h -r", @@ -33,16 +33,21 @@ "yjs": "^13.6.8" }, "devDependencies": { + "@types/number-to-words": "^1.2.2", + "@types/pluralize": "^0.0.32", "@vitejs/plugin-vue": "^4.2.3", "@vue/cli-plugin-router": "~5.0.0", "autoprefixer": "^10.4.15", "postcss": "^8.4.29", "tailwindcss": "^3.3.3", + "typescript": "^5.2.2", "vite": "^4.4.5", - "vite-plugin-pwa": "^0.16.5" + "vite-plugin-pwa": "^0.16.5", + "vue-tsc": "^1.8.20" }, "prettier": { "singleQuote": true, - "printWidth": 100 + "printWidth": 100, + "arrowParens": "avoid" } } diff --git a/src/classes/insight.js b/src/classes/insight.ts similarity index 61% rename from src/classes/insight.js rename to src/classes/insight.ts index 7ea9513..d0da157 100644 --- a/src/classes/insight.js +++ b/src/classes/insight.ts @@ -1,32 +1,19 @@ -import { record } from "../store/record"; -import pluralize from "pluralize"; +import { record } from '../store/record'; +import pluralize from 'pluralize'; import n2w from 'number-to-words'; class Insight { - - /** - * The analyzed object - */ - #object; - /** * The analyzed person */ - person; + person: Person; - /** - * @param {import("../typedefs").Vital | import("../typedefs").Measurement} object Object to analyze - * @param {import("../typedefs").Person} person Person to analyze - */ - constructor(object, person) { - this.#object = object - this.person = person + constructor(person: Person) { + this.person = person; } /** * Get the text insight for this object - * - * @returns {string} */ get description() { return ''; @@ -34,49 +21,52 @@ class Insight { } export class VitalInsight extends Insight { - /** * The analyzed vital */ - vital; + vital: Vital; - /** - * @param {import("../typedefs").Vital} object Vital to be analyzed - * @param {import("../typedefs").Person} person Person to analyze - */ - constructor(object, person) { - super(object, person); + constructor(object: Vital, person: Person) { + super(person); this.vital = object; } /** * Get person's measurements for this vital - * + * * @returns {import("../typedefs").Measurement[]} */ get #measurements() { + if (!record.value) return []; if (!this.vital.low && !this.vital.high) return []; return record.value.measurements - .filter(measurement => measurement.personId === this.person.id && measurement.vitalId === this.vital.id) + .filter( + measurement => + measurement.personId === this.person.id && measurement.vitalId === this.vital.id + ) .toSorted((a, b) => b.date - a.date); } /** * Measurements whose recent values are below the low value for this vital - * + * * @returns {import("../typedefs").Measurement[]} */ get #lowMeasurements() { - return this.#measurements.slice(0, 5).filter(measurement => measurement.value < this.vital.low); + if (!this.vital.low) return []; + const low = this.vital.low; + return this.#measurements.slice(0, 5).filter(measurement => measurement.value < low); } - + /** * Measurements whose recent values are above the high value for this vital - * + * * @returns {import("../typedefs").Measurement[]} */ get #highMeasurements() { - return this.#measurements.slice(0, 5).filter(measurement => measurement.value > this.vital.high); + if (!this.vital.high) return []; + const high = this.vital.high; + return this.#measurements.slice(0, 5).filter(measurement => measurement.value > high); } /** @@ -104,11 +94,11 @@ export class VitalInsight extends Insight { /** * Get the text description of this vital's Insight - * + * * @returns {string} */ get description() { - if (!this.level) return null; + if (!this.level) return ''; let text = `Recent ${this.vital.name} measurements`; @@ -124,9 +114,15 @@ export class VitalInsight extends Insight { break; } - if ( (this.level === 'low' && this.trend === 1) || (this.level === 'high' && this.trend === -1) ) { + if ( + (this.level === 'low' && this.trend === 1) || + (this.level === 'high' && this.trend === -1) + ) { text += ', but are'; - } else if ( (this.level === 'low' && this.trend === -1) || (this.level === 'high' && this.trend === 1) ) { + } else if ( + (this.level === 'low' && this.trend === -1) || + (this.level === 'high' && this.trend === 1) + ) { text += ' and are'; } else if (this.trend !== 0) { text += ' and are'; @@ -146,21 +142,23 @@ export class VitalInsight extends Insight { } export class VitalInsightsSummary { + person: Person; - /** - * @param {import("../typedefs").Person} person Person whose vitals are being analyzed - */ - constructor(person) { - this.person = person + constructor(person: Person) { + this.person = person; } get #measurements() { + if (!record.value) return []; return record.value.measurements.filter(measurement => measurement.personId === this.person.id); } get #vitals() { + if (!record.value) return []; + let vitalIds = this.#measurements.map(measurement => measurement.vitalId); vitalIds = [...new Set(vitalIds)]; + return record.value.vitals.filter(vital => vitalIds.includes(vital.id)); } @@ -195,15 +193,30 @@ export class VitalInsightsSummary { const vitalLevelsDescriptions = []; if (this.#normalLevelVitals.length > 0) { - vitalLevelsDescriptions.push(`${n2w.toWords(this.#normalLevelVitals.length)} ${pluralize('vital', this.#normalLevelVitals.length)} ${pluralize('has', this.#normalLevelVitals.length)} normal levels`); + vitalLevelsDescriptions.push( + `${n2w.toWords(this.#normalLevelVitals.length)} ${pluralize( + 'vital', + this.#normalLevelVitals.length + )} ${pluralize('has', this.#normalLevelVitals.length)} normal levels` + ); } if (this.#lowLevelVitals.length > 0) { - vitalLevelsDescriptions.push(`${n2w.toWords(this.#lowLevelVitals.length)} ${pluralize('vital', this.#lowLevelVitals.length)} ${pluralize('has', this.#lowLevelVitals.length)} low levels`); + vitalLevelsDescriptions.push( + `${n2w.toWords(this.#lowLevelVitals.length)} ${pluralize( + 'vital', + this.#lowLevelVitals.length + )} ${pluralize('has', this.#lowLevelVitals.length)} low levels` + ); } if (this.#highLevelVitals.length > 0) { - vitalLevelsDescriptions.push(`${n2w.toWords(this.#highLevelVitals.length)} ${pluralize('vital', this.#highLevelVitals.length)} ${pluralize('has', this.#highLevelVitals.length)} high levels`); + vitalLevelsDescriptions.push( + `${n2w.toWords(this.#highLevelVitals.length)} ${pluralize( + 'vital', + this.#highLevelVitals.length + )} ${pluralize('has', this.#highLevelVitals.length)} high levels` + ); } // Trends @@ -213,11 +226,21 @@ export class VitalInsightsSummary { const vitalTrendsDescriptions = []; if (this.#upwardTrendVitals.length > 0) { - vitalTrendsDescriptions.push(`${n2w.toWords(this.#upwardTrendVitals.length)} ${pluralize('vital', this.#upwardTrendVitals.length)} ${pluralize('is', this.#upwardTrendVitals.length)} trending upward`); + vitalTrendsDescriptions.push( + `${n2w.toWords(this.#upwardTrendVitals.length)} ${pluralize( + 'vital', + this.#upwardTrendVitals.length + )} ${pluralize('is', this.#upwardTrendVitals.length)} trending upward` + ); } if (this.#downwardTrendVitals.length > 0) { - vitalTrendsDescriptions.push(`${n2w.toWords(this.#downwardTrendVitals.length)} ${pluralize('vital', this.#downwardTrendVitals.length)} ${pluralize('is', this.#downwardTrendVitals.length)} trending downward`); + vitalTrendsDescriptions.push( + `${n2w.toWords(this.#downwardTrendVitals.length)} ${pluralize( + 'vital', + this.#downwardTrendVitals.length + )} ${pluralize('is', this.#downwardTrendVitals.length)} trending downward` + ); } const trendsDescription = vitalTrendsDescriptions.join(', '); @@ -230,38 +253,34 @@ export class VitalInsightsSummary { /** * Calculate non-parametric regression - * - * @param {Array} values Values to consider - * @param {Number} bandwidth Bandwidth - * @returns {Number[]} */ -const nonParametricRegression = (values, bandwidth = 0.5) => { +const nonParametricRegression = (values: number[], bandwidth = 0.5) => { // Create a kernel function. - const kernelFunction = (x) => Math.exp(-(Math.pow(x, 2)) / (2 * Math.pow(bandwidth, 2))); + const kernelFunction = (x: number) => Math.exp(-Math.pow(x, 2) / (2 * Math.pow(bandwidth, 2))); // Calculate the weighted average of the data points. - const predictedValues = values.map((yi, i) => { - const weights = values.map((yj, j) => kernelFunction((yi - yj) / bandwidth)); - return values.reduce((acc, yj, j) => acc + weights[j] * yj, 0) / weights.reduce((acc, w) => acc + w, 0); + const predictedValues = values.map(yi => { + const weights = values.map(yj => kernelFunction((yi - yj) / bandwidth)); + return ( + values.reduce((acc, yj, j) => acc + weights[j] * yj, 0) / + weights.reduce((acc, w) => acc + w, 0) + ); }); // Return the predicted values. return predictedValues; -} +}; /** * Calculate trend from values - * - * @param {Array} values Values to consider - * @param {Number} bandwidth Bandwidth - * @returns {1 | 0 | -1} */ -const nonParametricRegressionTrend = (values, bandwidth = 0.5) => { +const nonParametricRegressionTrend = (values: number[], bandwidth = 0.5) => { // Calculate the predicted values using the nonParametricRegression function. const predictedValues = nonParametricRegression(values, bandwidth); // Calculate the slope of the predicted values. - const slope = (predictedValues[predictedValues.length - 1] - predictedValues[0]) / predictedValues.length; + const slope = + (predictedValues[predictedValues.length - 1] - predictedValues[0]) / predictedValues.length; // Return 1 if the trend is up, 0 if the trend is flat, and -1 if the trend is down. if (slope > 0) { @@ -271,4 +290,4 @@ const nonParametricRegressionTrend = (values, bandwidth = 0.5) => { } else { return 0; } -} \ No newline at end of file +}; diff --git a/src/classes/logEntry.js b/src/classes/logEntry.js deleted file mode 100644 index 200c2b7..0000000 --- a/src/classes/logEntry.js +++ /dev/null @@ -1,89 +0,0 @@ -import { people } from "../store/people"; -import { vitals } from "../store/vitals" -import { notes } from "../store/notes"; - -export class LogEntry { - - /** - * - * @param {string} personId - The ID of the {@link Person} associated with this log entry. - */ - constructor(personId) { - this.personId = personId; - this.objectId = this.personId; - } - - /** @type {number} */ - date; - - /** @type {string} */ - objectId; - - get person() { - return people.value.find(person => person.id === this.personId); - } - - get personName() { - return this.person.firstName; - } - - get note() { - return null; - } - - get description() { - return null; - } -} - -export class MeasurementLogEntry extends LogEntry { - - /** - * Create LogEntry from {@link Measurement} - * @param {import("../typedefs").Measurement} measurement - The {@link Measurement} associated with this log entry. - */ - constructor(measurement) { - super(measurement.personId); - this.measurement = measurement; - this.objectId = this.measurement.id; - this.date = this.measurement.created || this.measurement.date; - } - - get vital() { - return vitals.value.find(vital => vital.id === this.measurement.vitalId); - } - - get note() { - if (!this.measurement.noteId) return null; - return notes.value.find(note => note.id === this.measurement.noteId) - } - - get description() { - return `${this.vital.name} measurement`; - } -} - -export class NoteLogEntry extends LogEntry { - - /** @type {import("../typedefs").Note} */ - #note - - /** - * Create LogEntry from {@link Note} - * @param {import("../typedefs").Note} note - The {@link Note} associated with this log entry - */ - constructor(note) { - super(note.personId); - this.#note = note; - this.objectId = note.id; - this.date = note.created; - } - - get description() { - return 'note'; - } - - get note() { - return this.#note - } -} \ No newline at end of file diff --git a/src/classes/logEntry.ts b/src/classes/logEntry.ts new file mode 100644 index 0000000..5a24cbd --- /dev/null +++ b/src/classes/logEntry.ts @@ -0,0 +1,76 @@ +import { people } from '../store/people'; +import { vitals } from '../store/vitals'; +import { notes } from '../store/notes'; + +export class LogEntry { + date: number; + + objectId: string; + + personId: string; + + constructor(personId: string) { + this.personId = personId; + this.objectId = this.personId; + this.date = Date.now(); + } + + get person() { + return people.value.find(person => person.id === this.personId); + } + + get personName() { + return this.person?.firstName; + } + + get note(): Note | undefined { + return undefined; + } + + get description(): string | undefined { + return undefined; + } +} + +export class MeasurementLogEntry extends LogEntry { + measurement: Measurement; + + constructor(measurement: Measurement) { + super(measurement.personId); + this.measurement = measurement; + this.objectId = this.measurement.id; + this.date = this.measurement.created || this.measurement.date; + } + + get vital() { + return vitals.value.find(vital => vital.id === this.measurement.vitalId); + } + + get note() { + if (!this.measurement.noteId) return undefined; + return notes.value.find(note => note.id === this.measurement.noteId); + } + + get description() { + return `${this.vital?.name} measurement`; + } +} + +export class NoteLogEntry extends LogEntry { + #note: Note; + + constructor(note: Note) { + super(note.personId); + this.#note = note; + this.objectId = note.id; + this.date = note.created; + } + + get description() { + return 'note'; + } + + get note() { + return this.#note; + } +} diff --git a/src/components/Dashboard/Dashboard.vue b/src/components/Dashboard/Dashboard.vue index 20f073c..9929270 100644 --- a/src/components/Dashboard/Dashboard.vue +++ b/src/components/Dashboard/Dashboard.vue @@ -1,8 +1,8 @@ - diff --git a/src/components/Layout.vue b/src/components/Layout.vue index b9fed82..c26b90c 100644 --- a/src/components/Layout.vue +++ b/src/components/Layout.vue @@ -1,4 +1,4 @@ -