From 77b369be6182aba0a69230ced17b9aa483fc0a4d Mon Sep 17 00:00:00 2001 From: ritikhaashok Date: Thu, 23 Apr 2026 13:45:58 -0500 Subject: [PATCH 1/4] changes --- package-lock.json | 111 +++++-- .../20260129194131_init/migration.sql | 66 ++++ .../20260217005051_add_gad/migration.sql | 36 ++ .../migrations/20260226040142/migration.sql | 123 +++++++ .../20260226042411_add_image/migration.sql | 203 ++++++++++++ .../migrations/20260226060301/migration.sql | 14 + .../20260226072412_appointments/migration.sql | 26 ++ .../migration.sql | 3 + .../migrations/20260409203356/migration.sql | 310 ++++++++++++++++++ 9 files changed, 873 insertions(+), 19 deletions(-) create mode 100644 prisma/migrations/20260129194131_init/migration.sql create mode 100644 prisma/migrations/20260217005051_add_gad/migration.sql create mode 100644 prisma/migrations/20260226040142/migration.sql create mode 100644 prisma/migrations/20260226042411_add_image/migration.sql create mode 100644 prisma/migrations/20260226060301/migration.sql create mode 100644 prisma/migrations/20260226072412_appointments/migration.sql create mode 100644 prisma/migrations/20260409184455_add_recurring_fields/migration.sql create mode 100644 prisma/migrations/20260409203356/migration.sql diff --git a/package-lock.json b/package-lock.json index 24be3b6..4acd9dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,8 +16,10 @@ "@prisma/adapter-better-sqlite3": "^7.2.0", "@prisma/client": "^7.2.0", "@tiptap/extension-text-align": "^3.20.1", + "@types/dompurify": "^3.2.0", "@types/nodemailer": "^7.0.4", "better-auth": "^1.4.7", + "dompurify": "^3.3.3", "dotenv": "^17.2.3", "marked": "^17.0.4", "nodemailer": "^7.0.11", @@ -90,6 +92,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -463,6 +466,7 @@ "version": "1.4.18", "resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.4.18.tgz", "integrity": "sha512-q+awYgC7nkLEBdx2sW0iJjkzgSHlIxGnOpsN1r/O1+a4m7osJNHtfK2mKJSL1I+GfNyIlxJF8WvD/NLuYMpmcg==", + "peer": true, "dependencies": { "@standard-schema/spec": "^1.0.0", "zod": "^4.3.5" @@ -492,12 +496,14 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.0.tgz", "integrity": "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@better-fetch/fetch": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/@better-fetch/fetch/-/fetch-1.1.21.tgz", - "integrity": "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==" + "integrity": "sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==", + "peer": true }, "node_modules/@bomb.sh/tab": { "version": "0.0.14", @@ -642,7 +648,8 @@ "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.3.15.tgz", "integrity": "sha512-Cj++n1Mekf9ETfdc16TlDi+cDDQF0W7EcbyRHYOAeZdsAe8M/FJg18itDTSwyHfar2WIezawM9o0EKaRGVKygQ==", "devOptional": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/@electric-sql/pglite-socket": { "version": "0.0.20", @@ -1128,6 +1135,7 @@ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" @@ -1889,6 +1897,7 @@ "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-4.4.2.tgz", "integrity": "sha512-5+IPRNX2CjkBhuWUwz0hBuLqiaJPRoKzQ+SvcdrQDbAyE+VDeFt74VpSFr5/R0ujrK4b+XnSHUJWdS72w6hsog==", "license": "MIT", + "peer": true, "dependencies": { "c12": "^3.3.3", "consola": "^3.4.2", @@ -3703,6 +3712,7 @@ "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.3.0.tgz", "integrity": "sha512-FXBIxirqQfdC6b6HnNgxGmU7ydCPEPk7maHMOduJJfnTP+MuOGa15X4omjR/zpPUUpm8ef/mEFQjJudOGkXFcQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@prisma/client-runtime-utils": "7.3.0" }, @@ -4857,6 +4867,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.20.5.tgz", "integrity": "sha512-Pkjd41UJ4F6Z8cPV+gEvqnt1VhY2g66xMjbpxREs0ECA5jRezCNKSZcc2pueQRTMtmn1SaSzGM9U/ifhVlVYOA==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4953,6 +4964,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-collaboration/-/extension-collaboration-3.20.5.tgz", "integrity": "sha512-IalIm6BznHds2VzR4+6gMAgi4VXwZAXdYkl28EPZ8/xscBaUn1tCxcTBbCpmN3UhkABaCoJtmmER5TDy+x72Ag==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -4982,6 +4994,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-drag-handle/-/extension-drag-handle-3.20.5.tgz", "integrity": "sha512-D2W2fkpmXKRG4i0K0XVfbsTRYQMU9RudQIXBB7HnYZFosyJs3dvsT1cFurDUANKLPCxbI24eiubAKs6b1vzGpA==", "license": "MIT", + "peer": true, "dependencies": { "@floating-ui/dom": "^1.6.13" }, @@ -5142,6 +5155,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.20.5.tgz", "integrity": "sha512-s+Y8Q7Orq+WQiwgFB/VPMYZe+6EAR2F69xCpvOynlzTInLO4cF6QpXomuGEYAZxLHe8ZBmeIaR7y8MH/OgjrDw==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5197,6 +5211,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extension-node-range/-/extension-node-range-3.20.5.tgz", "integrity": "sha512-6CbgZULF+dQ9KTothAORBZAXPdGneWicMWTV3Gyeh9gNySC18QsGQj3D2GxnllpMekmZXydtDbNFSoCiWjKFWQ==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5302,6 +5317,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.20.5.tgz", "integrity": "sha512-c4am6SznqfMnbUNSh4MvufiD7cMLdqL1BArok22uBgSWkS1sB9RVBYe8+x0jrOkk0UPEVlzDHbQ+nU+WmIyS2Q==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5333,6 +5349,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.20.5.tgz", "integrity": "sha512-yJhDa7Chx2EqJMX/jlewBv0za7slf1dKHWYve1XaApuVHEkxl0Ul3EDbwnx316vIITkuFW/pWSwkSsAplyBeCw==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", @@ -5399,6 +5416,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/suggestion/-/suggestion-3.20.5.tgz", "integrity": "sha512-5fqRNgnzYdJ1oDpyLqwrbVsZwvI+5VW/U89LPMvBYM7sFS7Xd0xfyxyAOWcJN4V0zLeTcuElWN3R+IUTLKbU+Q==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5413,6 +5431,7 @@ "resolved": "https://registry.npmjs.org/@tiptap/vue-3/-/vue-3-3.20.5.tgz", "integrity": "sha512-5uUK3RAMNvUetZOv56Kz8nurhxHxMH60GgCCrVFgIBZoTc14u3d3v7EpcA6gNgzogutrR8GxvyFU3iIkj4kkHA==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -5469,6 +5488,16 @@ "@types/node": "*" } }, + "node_modules/@types/dompurify": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz", + "integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==", + "deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.", + "license": "MIT", + "dependencies": { + "dompurify": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -5532,6 +5561,13 @@ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "license": "MIT" }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/web-bluetooth": { "version": "0.0.21", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", @@ -5868,6 +5904,7 @@ "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-14.2.1.tgz", "integrity": "sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.2.1", @@ -5993,6 +6030,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6333,6 +6371,7 @@ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "license": "Apache-2.0", + "peer": true, "peerDependencies": { "bare-abort-controller": "*" }, @@ -6556,6 +6595,7 @@ "resolved": "https://registry.npmjs.org/better-call/-/better-call-1.1.8.tgz", "integrity": "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==", "license": "MIT", + "peer": true, "dependencies": { "@better-auth/utils": "^0.3.0", "@better-fetch/fetch": "^1.1.4", @@ -6701,6 +6741,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6802,6 +6843,7 @@ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -6919,6 +6961,7 @@ "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", "license": "MIT", + "peer": true, "dependencies": { "consola": "^3.2.3" } @@ -6994,15 +7037,6 @@ "integrity": "sha512-Z3UPUKasUVDFCDYAjP2fmlVRf1jFHJv1izAmPjiOa0OCIw1W7iC8PZ2GsoDa8uZv+mKyWopxxStT9q05+27h7w==", "license": "Apache-2.0" }, - "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -7595,6 +7629,15 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.1.tgz", + "integrity": "sha512-JahakDAIg1gyOm7dlgWSDjV4n7Ip2PKR55NIT6jrMfIgLFgWo81vdr1/QGqWtFNRqXP9UV71oVePtjqS2ebnPw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", @@ -7675,7 +7718,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-auto-height": { "version": "8.6.0", @@ -8252,6 +8296,7 @@ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz", "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=10" } @@ -8505,6 +8550,7 @@ "integrity": "sha512-U7tt8JsyrxSRKspfhtLET79pU8K+tInj5QZXs1jSugO1Vq5dFj3kmZsRldo29mTBfcjDRVRXrEZ6LS63Cog9ZA==", "devOptional": true, "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -8898,7 +8944,6 @@ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", "license": "MIT", - "peer": true, "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" @@ -8933,6 +8978,7 @@ "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/panva" } @@ -8996,6 +9042,7 @@ "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.28.10.tgz", "integrity": "sha512-ksNxfzIW77OcZ+QWSAPC7yDqUSaIVwkTWnTPNiIy//vifNbwsSgQ57OkkncHxxpcBHM3LRfLAZVEh7kjq5twVA==", "license": "MIT", + "peer": true, "engines": { "node": ">=20.0.0" } @@ -9057,7 +9104,6 @@ "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.117.tgz", "integrity": "sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw==", "license": "MIT", - "peer": true, "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -9811,6 +9857,7 @@ "integrity": "sha512-FBrGau0IXmuqg4haEZRBfHNWB5mUARw6hNwPDXXGg0XzVJ50mr/9hb267lvpVMnhZ1FON3qNd4Xfcez1rbFwSg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", @@ -9867,6 +9914,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": "^20.0.0 || >=22.0.0" } @@ -10203,6 +10251,7 @@ "resolved": "https://registry.npmjs.org/nuxt/-/nuxt-4.4.2.tgz", "integrity": "sha512-iWVFpr/YEqVU/CenqIHMnIkvb2HE/9f+q8oxZ+pj2et+60NljGRClCgnmbvGPdmNFE0F1bEhoBCYfqbDOCim3Q==", "license": "MIT", + "peer": true, "dependencies": { "@dxup/nuxt": "^0.4.0", "@nuxt/cli": "^3.34.0", @@ -10555,6 +10604,7 @@ "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.117.0.tgz", "integrity": "sha512-l3cbgK5wUvWDVNWM/JFU77qDdGZK1wudnLsFcrRyNo/bL1CyU8pC25vDhMHikVY29lbK2InTWsX42RxVSutUdQ==", "license": "MIT", + "peer": true, "dependencies": { "@oxc-project/types": "^0.117.0" }, @@ -10754,6 +10804,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11242,7 +11293,6 @@ "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -11280,6 +11330,7 @@ "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -11388,6 +11439,7 @@ "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@prisma/config": "7.3.0", "@prisma/dev": "0.20.0", @@ -11561,6 +11613,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", "license": "MIT", + "peer": true, "dependencies": { "orderedmap": "^2.0.0" } @@ -11590,6 +11643,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", @@ -11650,6 +11704,7 @@ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.7.tgz", "integrity": "sha512-jUwKNCEIGiqdvhlS91/2QAg21e4dfU5bH2iwmSDQeosXJgKF7smG0YSplOWK0cjSNgIqXe7VXqo7EIfUFJdt3w==", "license": "MIT", + "peer": true, "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", @@ -12010,6 +12065,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -12296,8 +12352,7 @@ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "devOptional": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/scule": { "version": "1.3.0", @@ -12827,6 +12882,15 @@ "url": "https://opencollective.com/svgo" } }, + "node_modules/svgo/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/system-architecture": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", @@ -12856,6 +12920,7 @@ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/dcastil" @@ -12884,7 +12949,8 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tapable": { "version": "2.3.2", @@ -13130,6 +13196,7 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -13610,6 +13677,7 @@ "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "typescript": ">=5" }, @@ -13732,6 +13800,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -14087,6 +14156,7 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz", "integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.31", "@vue/compiler-sfc": "3.5.31", @@ -14129,6 +14199,7 @@ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==", "license": "MIT", + "peer": true, "dependencies": { "@vue/devtools-api": "^6.6.4" }, @@ -14327,6 +14398,7 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -14443,6 +14515,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/prisma/migrations/20260129194131_init/migration.sql b/prisma/migrations/20260129194131_init/migration.sql new file mode 100644 index 0000000..b547781 --- /dev/null +++ b/prisma/migrations/20260129194131_init/migration.sql @@ -0,0 +1,66 @@ +-- CreateTable +CREATE TABLE "user" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "emailVerified" BOOLEAN NOT NULL DEFAULT false, + "image" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "session" ( + "id" TEXT NOT NULL PRIMARY KEY, + "expiresAt" DATETIME NOT NULL, + "token" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "ipAddress" TEXT, + "userAgent" TEXT, + "userId" TEXT NOT NULL, + CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "account" ( + "id" TEXT NOT NULL PRIMARY KEY, + "accountId" TEXT NOT NULL, + "providerId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "accessToken" TEXT, + "refreshToken" TEXT, + "idToken" TEXT, + "accessTokenExpiresAt" DATETIME, + "refreshTokenExpiresAt" DATETIME, + "scope" TEXT, + "password" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "verification" ( + "id" TEXT NOT NULL PRIMARY KEY, + "identifier" TEXT NOT NULL, + "value" TEXT NOT NULL, + "expiresAt" DATETIME NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "user_email_key" ON "user"("email"); + +-- CreateIndex +CREATE INDEX "session_userId_idx" ON "session"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "session_token_key" ON "session"("token"); + +-- CreateIndex +CREATE INDEX "account_userId_idx" ON "account"("userId"); + +-- CreateIndex +CREATE INDEX "verification_identifier_idx" ON "verification"("identifier"); diff --git a/prisma/migrations/20260217005051_add_gad/migration.sql b/prisma/migrations/20260217005051_add_gad/migration.sql new file mode 100644 index 0000000..278fd8e --- /dev/null +++ b/prisma/migrations/20260217005051_add_gad/migration.sql @@ -0,0 +1,36 @@ +-- CreateTable +CREATE TABLE "GadForm" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'IN_PROGRESS', + "totalScore" INTEGER, + "severity" TEXT, + "submittedAt" DATETIME, + CONSTRAINT "GadForm_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "GadQuestion" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "formId" INTEGER NOT NULL, + "userId" TEXT NOT NULL, + "g01" INTEGER, + "g02" INTEGER, + "g03" INTEGER, + "g04" INTEGER, + "g05" INTEGER, + "g06" INTEGER, + "g07" INTEGER, + "g08" INTEGER, + CONSTRAINT "GadQuestion_formId_fkey" FOREIGN KEY ("formId") REFERENCES "GadForm" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "GadQuestion_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateIndex +CREATE INDEX "GadForm_userId_idx" ON "GadForm"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GadQuestion_formId_key" ON "GadQuestion"("formId"); + +-- CreateIndex +CREATE INDEX "GadQuestion_userId_idx" ON "GadQuestion"("userId"); diff --git a/prisma/migrations/20260226040142/migration.sql b/prisma/migrations/20260226040142/migration.sql new file mode 100644 index 0000000..38b3e29 --- /dev/null +++ b/prisma/migrations/20260226040142/migration.sql @@ -0,0 +1,123 @@ +/* + Warnings: + + - You are about to drop the `GadForm` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `GadQuestion` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the column `image` on the `user` table. All the data in the column will be lost. + +*/ +-- DropIndex +DROP INDEX "GadForm_userId_idx"; + +-- DropIndex +DROP INDEX "GadQuestion_userId_idx"; + +-- DropIndex +DROP INDEX "GadQuestion_formId_key"; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "GadForm"; +PRAGMA foreign_keys=on; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "GadQuestion"; +PRAGMA foreign_keys=on; + +-- CreateTable +CREATE TABLE "AppForm" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'IN_PROGRESS', + "submittedAt" DATETIME, + CONSTRAINT "AppForm_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "AppQuestion" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "formId" INTEGER NOT NULL, + "userId" TEXT NOT NULL, + "q01" TEXT, + "q02" TEXT, + "q03" TEXT, + "q04" TEXT, + "q05" TEXT, + "q06" TEXT, + "q07" TEXT, + "q08" TEXT, + "q09" TEXT, + "q10" TEXT, + "q11" TEXT, + "q12" TEXT, + "q13" TEXT, + "q14" TEXT, + "q15" TEXT, + "q16" TEXT, + "q17" TEXT, + "q18" TEXT, + "q19" TEXT, + "q20" TEXT, + "q21" TEXT, + "q22" TEXT, + "q23" TEXT, + "q24" TEXT, + "q25" TEXT, + "q26" TEXT, + "q27" TEXT, + "q28" TEXT, + "q29" TEXT, + "q30" TEXT, + "q31" TEXT, + "q32" TEXT, + "q33" TEXT, + "q34" TEXT, + "q35" TEXT, + "q36" TEXT, + "q37" TEXT, + "q38" TEXT, + "q39" TEXT, + "q40" TEXT, + "q41" TEXT, + "q42" TEXT, + "q43" TEXT, + "q44" TEXT, + "q45" TEXT, + "q46" TEXT, + "q47" TEXT, + "q48" TEXT, + "q49" TEXT, + "q50" TEXT, + CONSTRAINT "AppQuestion_formId_fkey" FOREIGN KEY ("formId") REFERENCES "AppForm" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "AppQuestion_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_user" ( + "id" TEXT NOT NULL PRIMARY KEY, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "emailVerified" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "role" TEXT NOT NULL DEFAULT 'CLIENT', + "phoneNumber" INTEGER +); +INSERT INTO "new_user" ("createdAt", "email", "emailVerified", "id", "name", "updatedAt") SELECT "createdAt", "email", "emailVerified", "id", "name", "updatedAt" FROM "user"; +DROP TABLE "user"; +ALTER TABLE "new_user" RENAME TO "user"; +CREATE UNIQUE INDEX "user_email_key" ON "user"("email"); +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; + +-- CreateIndex +CREATE UNIQUE INDEX "AppForm_userId_key" ON "AppForm"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "AppQuestion_formId_key" ON "AppQuestion"("formId"); + +-- CreateIndex +CREATE INDEX "AppQuestion_userId_idx" ON "AppQuestion"("userId"); diff --git a/prisma/migrations/20260226042411_add_image/migration.sql b/prisma/migrations/20260226042411_add_image/migration.sql new file mode 100644 index 0000000..b34507c --- /dev/null +++ b/prisma/migrations/20260226042411_add_image/migration.sql @@ -0,0 +1,203 @@ +-- AlterTable +ALTER TABLE "user" ADD COLUMN "image" TEXT; + +-- CreateTable +CREATE TABLE "form" ( + "id" TEXT NOT NULL PRIMARY KEY, + "title" TEXT NOT NULL, + "description" TEXT, + "slug" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "question" ( + "id" TEXT NOT NULL PRIMARY KEY, + "text" TEXT NOT NULL, + "type" TEXT NOT NULL, + "alias" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "form_question" ( + "id" TEXT NOT NULL PRIMARY KEY, + "formId" TEXT NOT NULL, + "questionId" TEXT NOT NULL, + "order" INTEGER NOT NULL, + CONSTRAINT "form_question_formId_fkey" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "form_question_questionId_fkey" FOREIGN KEY ("questionId") REFERENCES "question" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "form_assignment" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "formId" TEXT NOT NULL, + "assignedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "completedAt" DATETIME, + CONSTRAINT "form_assignment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "form_assignment_formId_fkey" FOREIGN KEY ("formId") REFERENCES "form" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "ace_response" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "responses" TEXT NOT NULL, + "completedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "ace_response_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "GadForm" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'IN_PROGRESS', + "totalScore" INTEGER, + "severity" TEXT, + "submittedAt" DATETIME, + CONSTRAINT "GadForm_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "GadQuestion" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "formId" INTEGER NOT NULL, + "userId" TEXT NOT NULL, + "g01" INTEGER, + "g02" INTEGER, + "g03" INTEGER, + "g04" INTEGER, + "g05" INTEGER, + "g06" INTEGER, + "g07" INTEGER, + "g08" INTEGER, + CONSTRAINT "GadQuestion_formId_fkey" FOREIGN KEY ("formId") REFERENCES "GadForm" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "GadQuestion_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "PhqForm" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'IN_PROGRESS', + "totalScore" INTEGER, + "submittedAt" DATETIME, + CONSTRAINT "PhqForm_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "PhqQuestion" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "formId" INTEGER NOT NULL, + "userId" TEXT NOT NULL, + "q1" INTEGER, + "q2" INTEGER, + "q3" INTEGER, + "q4" INTEGER, + "q5" INTEGER, + "q6" INTEGER, + "q7" INTEGER, + "q8" INTEGER, + "q9" INTEGER, + CONSTRAINT "PhqQuestion_formId_fkey" FOREIGN KEY ("formId") REFERENCES "PhqForm" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "PhqQuestion_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "PclForm" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'IN_PROGRESS', + "submittedAt" DATETIME, + CONSTRAINT "PclForm_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "PclQuestion" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "formId" INTEGER NOT NULL, + "userId" TEXT NOT NULL, + "q01" INTEGER, + "q02" INTEGER, + "q03" INTEGER, + "q04" INTEGER, + "q05" INTEGER, + "q06" INTEGER, + "q07" INTEGER, + "q08" INTEGER, + "q09" INTEGER, + "q10" INTEGER, + "q11" INTEGER, + "q12" INTEGER, + "q13" INTEGER, + "q14" INTEGER, + "q15" INTEGER, + "q16" INTEGER, + "q17" INTEGER, + "q18" INTEGER, + "q19" INTEGER, + "q20" INTEGER, + CONSTRAINT "PclQuestion_formId_fkey" FOREIGN KEY ("formId") REFERENCES "PclForm" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "PclQuestion_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "form_slug_key" ON "form"("slug"); + +-- CreateIndex +CREATE UNIQUE INDEX "question_alias_key" ON "question"("alias"); + +-- CreateIndex +CREATE INDEX "form_question_formId_idx" ON "form_question"("formId"); + +-- CreateIndex +CREATE INDEX "form_question_questionId_idx" ON "form_question"("questionId"); + +-- CreateIndex +CREATE UNIQUE INDEX "form_question_formId_questionId_key" ON "form_question"("formId", "questionId"); + +-- CreateIndex +CREATE INDEX "form_assignment_userId_idx" ON "form_assignment"("userId"); + +-- CreateIndex +CREATE INDEX "form_assignment_formId_idx" ON "form_assignment"("formId"); + +-- CreateIndex +CREATE UNIQUE INDEX "form_assignment_userId_formId_key" ON "form_assignment"("userId", "formId"); + +-- CreateIndex +CREATE INDEX "ace_response_userId_idx" ON "ace_response"("userId"); + +-- CreateIndex +CREATE INDEX "GadForm_userId_idx" ON "GadForm"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "GadQuestion_formId_key" ON "GadQuestion"("formId"); + +-- CreateIndex +CREATE INDEX "GadQuestion_userId_idx" ON "GadQuestion"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "PhqForm_userId_key" ON "PhqForm"("userId"); + +-- CreateIndex +CREATE INDEX "PhqForm_userId_idx" ON "PhqForm"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "PhqQuestion_formId_key" ON "PhqQuestion"("formId"); + +-- CreateIndex +CREATE INDEX "PhqQuestion_userId_idx" ON "PhqQuestion"("userId"); + +-- CreateIndex +CREATE INDEX "PclForm_userId_idx" ON "PclForm"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "PclQuestion_formId_key" ON "PclQuestion"("formId"); + +-- CreateIndex +CREATE INDEX "PclQuestion_userId_idx" ON "PclQuestion"("userId"); diff --git a/prisma/migrations/20260226060301/migration.sql b/prisma/migrations/20260226060301/migration.sql new file mode 100644 index 0000000..d40952a --- /dev/null +++ b/prisma/migrations/20260226060301/migration.sql @@ -0,0 +1,14 @@ +-- CreateTable +CREATE TABLE "client" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'INCOMPLETE', + "therapyWeek" INTEGER, + CONSTRAINT "client_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "client_userId_key" ON "client"("userId"); + +-- CreateIndex +CREATE INDEX "client_status_idx" ON "client"("status"); diff --git a/prisma/migrations/20260226072412_appointments/migration.sql b/prisma/migrations/20260226072412_appointments/migration.sql new file mode 100644 index 0000000..e8d585f --- /dev/null +++ b/prisma/migrations/20260226072412_appointments/migration.sql @@ -0,0 +1,26 @@ +/* + Warnings: + + - You are about to drop the `client` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "client"; +PRAGMA foreign_keys=on; + +-- CreateTable +CREATE TABLE "Appointment" ( + "id" TEXT NOT NULL PRIMARY KEY, + "clientId" TEXT NOT NULL, + "adminId" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "startTime" DATETIME NOT NULL, + "endTime" DATETIME NOT NULL, + "status" TEXT NOT NULL DEFAULT 'SCHEDULED', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Appointment_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "Appointment_adminId_fkey" FOREIGN KEY ("adminId") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); diff --git a/prisma/migrations/20260409184455_add_recurring_fields/migration.sql b/prisma/migrations/20260409184455_add_recurring_fields/migration.sql new file mode 100644 index 0000000..c2580e5 --- /dev/null +++ b/prisma/migrations/20260409184455_add_recurring_fields/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Appointment" ADD COLUMN "recurrence" TEXT; +ALTER TABLE "Appointment" ADD COLUMN "seriesId" TEXT; diff --git a/prisma/migrations/20260409203356/migration.sql b/prisma/migrations/20260409203356/migration.sql new file mode 100644 index 0000000..ffdd9af --- /dev/null +++ b/prisma/migrations/20260409203356/migration.sql @@ -0,0 +1,310 @@ +/* + Warnings: + + - You are about to drop the `ace_response` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `form` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `form_assignment` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `form_question` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the `question` table. If the table is not empty, all the data it contains will be lost. + - You are about to drop the column `recurrence` on the `Appointment` table. All the data in the column will be lost. + - You are about to drop the column `seriesId` on the `Appointment` table. All the data in the column will be lost. + +*/ +-- DropIndex +DROP INDEX "ace_response_userId_idx"; + +-- DropIndex +DROP INDEX "form_slug_key"; + +-- DropIndex +DROP INDEX "form_assignment_userId_formId_key"; + +-- DropIndex +DROP INDEX "form_assignment_formId_idx"; + +-- DropIndex +DROP INDEX "form_assignment_userId_idx"; + +-- DropIndex +DROP INDEX "form_question_formId_questionId_key"; + +-- DropIndex +DROP INDEX "form_question_questionId_idx"; + +-- DropIndex +DROP INDEX "form_question_formId_idx"; + +-- DropIndex +DROP INDEX "question_alias_key"; + +-- AlterTable +ALTER TABLE "PclForm" ADD COLUMN "severity" TEXT; +ALTER TABLE "PclForm" ADD COLUMN "totalScore" INTEGER; + +-- AlterTable +ALTER TABLE "PclQuestion" ADD COLUMN "worstEvent" TEXT; + +-- AlterTable +ALTER TABLE "PhqForm" ADD COLUMN "severity" TEXT; + +-- AlterTable +ALTER TABLE "PhqQuestion" ADD COLUMN "q10" INTEGER; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "ace_response"; +PRAGMA foreign_keys=on; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "form"; +PRAGMA foreign_keys=on; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "form_assignment"; +PRAGMA foreign_keys=on; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "form_question"; +PRAGMA foreign_keys=on; + +-- DropTable +PRAGMA foreign_keys=off; +DROP TABLE "question"; +PRAGMA foreign_keys=on; + +-- CreateTable +CREATE TABLE "client" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'INCOMPLETE', + "therapyWeek" INTEGER, + "missedSessions" INTEGER NOT NULL DEFAULT 0, + CONSTRAINT "client_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "client_permission" ( + "id" TEXT NOT NULL PRIMARY KEY, + "clientId" TEXT NOT NULL, + "canViewScores" BOOLEAN NOT NULL DEFAULT false, + "canViewNotes" BOOLEAN NOT NULL DEFAULT false, + "canViewPlan" BOOLEAN NOT NULL DEFAULT false, + CONSTRAINT "client_permission_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "client" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "client_plan" ( + "id" TEXT NOT NULL PRIMARY KEY, + "clientId" TEXT NOT NULL, + "content" TEXT NOT NULL DEFAULT '', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "client_plan_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "client" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "change_audit" ( + "id" TEXT NOT NULL PRIMARY KEY, + "entityType" TEXT NOT NULL, + "entityId" TEXT NOT NULL, + "oldValue" TEXT, + "newValue" TEXT, + "reasoning" TEXT, + "documentationPath" TEXT, + "documentationName" TEXT, + "signatureData" TEXT NOT NULL, + "signedById" TEXT NOT NULL, + "signedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "change_audit_signedById_fkey" FOREIGN KEY ("signedById") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "ace_form" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'IN_PROGRESS', + "totalScore" INTEGER, + "severity" TEXT, + "submittedAt" DATETIME, + CONSTRAINT "ace_form_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "ace_question" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "formId" INTEGER NOT NULL, + "userId" TEXT NOT NULL, + "a01" TEXT, + "a02" TEXT, + "a03" TEXT, + "a04" TEXT, + "a05" TEXT, + "a06" TEXT, + "a07" TEXT, + "a08" TEXT, + "a09" TEXT, + "a10" TEXT, + CONSTRAINT "ace_question_formId_fkey" FOREIGN KEY ("formId") REFERENCES "ace_form" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "ace_question_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "PhysicianStatementForm" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'NOT_SUBMITTED', + "originalFileName" TEXT, + "storedFileName" TEXT, + "mimeType" TEXT, + "uploadedAt" DATETIME, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "PhysicianStatementForm_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "ReleaseOfInformationAuthorizationForm" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'NOT_SUBMITTED', + "originalFileName" TEXT, + "storedFileName" TEXT, + "mimeType" TEXT, + "uploadedAt" DATETIME, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "ReleaseOfInformationAuthorizationForm_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "session_note" ( + "id" TEXT NOT NULL PRIMARY KEY, + "clientId" TEXT NOT NULL, + "content" TEXT NOT NULL, + "attended" BOOLEAN NOT NULL DEFAULT true, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "session_note_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "client" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "session_note_edit" ( + "id" TEXT NOT NULL PRIMARY KEY, + "sessionNoteId" TEXT NOT NULL, + "originalContent" TEXT, + "reason" TEXT, + "signature" TEXT, + "editedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "session_note_edit_sessionNoteId_fkey" FOREIGN KEY ("sessionNoteId") REFERENCES "session_note" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "declaration_template" ( + "id" TEXT NOT NULL PRIMARY KEY, + "requestKind" TEXT NOT NULL, + "version" INTEGER NOT NULL, + "content" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP +); + +-- CreateTable +CREATE TABLE "session_notes_request" ( + "id" TEXT NOT NULL PRIMARY KEY, + "clientId" TEXT NOT NULL, + "requestKind" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'PENDING', + "signatureData" TEXT NOT NULL, + "declarationTemplateId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "decidedAt" DATETIME, + "decidedByUserId" TEXT, + "rejectionReason" TEXT, + "approvedSummaryText" TEXT, + CONSTRAINT "session_notes_request_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "client" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "session_notes_request_declarationTemplateId_fkey" FOREIGN KEY ("declarationTemplateId") REFERENCES "declaration_template" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "session_notes_request_decidedByUserId_fkey" FOREIGN KEY ("decidedByUserId") REFERENCES "user" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Appointment" ( + "id" TEXT NOT NULL PRIMARY KEY, + "clientId" TEXT NOT NULL, + "adminId" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "startTime" DATETIME NOT NULL, + "endTime" DATETIME NOT NULL, + "status" TEXT NOT NULL DEFAULT 'SCHEDULED', + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Appointment_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "Appointment_adminId_fkey" FOREIGN KEY ("adminId") REFERENCES "user" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); +INSERT INTO "new_Appointment" ("adminId", "clientId", "createdAt", "description", "endTime", "id", "startTime", "status", "title", "updatedAt") SELECT "adminId", "clientId", "createdAt", "description", "endTime", "id", "startTime", "status", "title", "updatedAt" FROM "Appointment"; +DROP TABLE "Appointment"; +ALTER TABLE "new_Appointment" RENAME TO "Appointment"; +CREATE INDEX "Appointment_clientId_idx" ON "Appointment"("clientId"); +CREATE INDEX "Appointment_adminId_idx" ON "Appointment"("adminId"); +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; + +-- CreateIndex +CREATE UNIQUE INDEX "client_userId_key" ON "client"("userId"); + +-- CreateIndex +CREATE INDEX "client_status_idx" ON "client"("status"); + +-- CreateIndex +CREATE UNIQUE INDEX "client_permission_clientId_key" ON "client_permission"("clientId"); + +-- CreateIndex +CREATE UNIQUE INDEX "client_plan_clientId_key" ON "client_plan"("clientId"); + +-- CreateIndex +CREATE INDEX "change_audit_entityType_entityId_idx" ON "change_audit"("entityType", "entityId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ace_form_userId_key" ON "ace_form"("userId"); + +-- CreateIndex +CREATE INDEX "ace_form_userId_idx" ON "ace_form"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ace_question_formId_key" ON "ace_question"("formId"); + +-- CreateIndex +CREATE INDEX "ace_question_userId_idx" ON "ace_question"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "PhysicianStatementForm_userId_key" ON "PhysicianStatementForm"("userId"); + +-- CreateIndex +CREATE INDEX "PhysicianStatementForm_status_idx" ON "PhysicianStatementForm"("status"); + +-- CreateIndex +CREATE UNIQUE INDEX "ReleaseOfInformationAuthorizationForm_userId_key" ON "ReleaseOfInformationAuthorizationForm"("userId"); + +-- CreateIndex +CREATE INDEX "ReleaseOfInformationAuthorizationForm_status_idx" ON "ReleaseOfInformationAuthorizationForm"("status"); + +-- CreateIndex +CREATE INDEX "session_note_clientId_idx" ON "session_note"("clientId"); + +-- CreateIndex +CREATE INDEX "session_note_edit_sessionNoteId_idx" ON "session_note_edit"("sessionNoteId"); + +-- CreateIndex +CREATE UNIQUE INDEX "declaration_template_requestKind_version_key" ON "declaration_template"("requestKind", "version"); + +-- CreateIndex +CREATE INDEX "session_notes_request_clientId_idx" ON "session_notes_request"("clientId"); + +-- CreateIndex +CREATE INDEX "session_notes_request_status_idx" ON "session_notes_request"("status"); + +-- CreateIndex +CREATE INDEX "session_notes_request_declarationTemplateId_idx" ON "session_notes_request"("declarationTemplateId"); From 04a26edc2b619c42f66fa566f9411c0232bb01ab Mon Sep 17 00:00:00 2001 From: ritikhaashok Date: Thu, 23 Apr 2026 15:28:33 -0500 Subject: [PATCH 2/4] create/delete reccuring events works on the CORRECT branch!! --- app/pages/calendar.vue | 137 ++++++++++++++++------ package-lock.json | 14 +++ package.json | 1 + prisma/schema/appointments.prisma | 4 + server/api/appointments/[id].delete.ts | 126 +++++++++++++-------- server/api/appointments/[id].put.ts | 56 +++++---- server/api/appointments/index.get.ts | 1 + server/api/appointments/index.post.ts | 150 +++++++++++++++++++------ 8 files changed, 344 insertions(+), 145 deletions(-) diff --git a/app/pages/calendar.vue b/app/pages/calendar.vue index b8b0435..bebfe86 100644 --- a/app/pages/calendar.vue +++ b/app/pages/calendar.vue @@ -15,6 +15,9 @@ | ({ id: string; role?: string } & Record) | null) ?? null ) + const deleteType = ref<'ONE' | 'FUTURE' | 'ALL' | null>(null) + const isDeleteTypeModalOpen = ref(false) + const isDeleteConfirmOpen = ref(false) const toast = useToast() const clients = ref([]) const events = ref([]) @@ -273,6 +276,7 @@ description: info.event.extendedProps?.description || info.event._def?.extendedProps?.description, status: info.event.extendedProps?.status || info.event._def?.extendedProps?.status, + seriesId: info.event.extendedProps.seriesId, } console.log('selectedEvent after click:', selectedEvent.value) @@ -296,17 +300,21 @@ async function saveEdit() { try { - await $fetch(`/api/appointments/${selectedEvent.value.id}`, { - method: 'PUT', - body: { - id: selectedEvent.value.id, - title: editForm.title, - description: editForm.description, - date: editForm.date, - startTime: editForm.startTime, - endTime: editForm.endTime, - }, - }) + async function saveEdit(type = 'ONE') { + await $fetch(`/api/appointments/${selectedEvent.value.id}`, { + method: 'PUT', + body: { + type, + seriesId: selectedEvent.value.seriesId, + startTime: selectedEvent.value.start, // IMPORTANT for FUTURE + title: editForm.title, + description: editForm.description, + date: editForm.date, + startTimeNew: editForm.startTime, + endTimeNew: editForm.endTime, + }, + }) + } toast.add({ title: 'Session updated', @@ -327,11 +335,38 @@ } } - async function deleteEvent() { + function onDeleteClick() { + // CLOSE the current modal first + isViewModalOpen.value = false + + setTimeout(() => { + if (selectedEvent.value?.seriesId) { + isDeleteTypeModalOpen.value = true + } else { + deleteType.value = 'ONE' + isDeleteConfirmOpen.value = true + } + }, 100) // small delay so UI updates cleanly + } + + function selectDeleteType(type: 'ONE' | 'FUTURE' | 'ALL') { + deleteType.value = type + isDeleteTypeModalOpen.value = false + + setTimeout(() => { + isDeleteConfirmOpen.value = true + }, 100) + } + + async function confirmDelete() { try { await $fetch(`/api/appointments/${selectedEvent.value.id}`, { method: 'DELETE', - credentials: 'include', + body: { + type: deleteType.value || 'ONE', // fallback safety + startTime: selectedEvent.value.start?.toISOString(), + seriesId: selectedEvent.value.seriesId || null, + }, }) toast.add({ @@ -339,12 +374,14 @@ color: 'success', }) - isDeleteConfirming.value = false + // reset state + deleteType.value = null + isDeleteConfirmOpen.value = false isViewModalOpen.value = false await loadEvents() } catch (error) { - console.error('Delete error:', error) + console.error(error) toast.add({ title: 'Failed to delete session', @@ -362,6 +399,7 @@ date: '', startTime: '', endTime: '', + recurrence: '', }) const clientOptions = computed(() => @@ -405,6 +443,8 @@ date: form.date, startTime: form.startTime, endTime: form.endTime, + isRecurring: !!form.recurrence, + recurrence: form.recurrence || null, }, }) @@ -416,11 +456,20 @@ closeCreateModal() await loadEvents() - } catch (error) { - console.error(error) + // } catch (error) { + // console.error(error) + + // toast.add({ + // title: 'Failed to create session', + // color: 'error', + // }) + // } + } catch (error: any) { + console.error('🔥 FULL ERROR:', error) + console.error('🔥 STACK:', error?.stack) toast.add({ - title: 'Failed to create session', + title: error?.data?.statusMessage || error?.message || 'Failed to create session', color: 'error', }) } @@ -525,7 +574,12 @@ - +
Cancel @@ -534,6 +588,35 @@
+ + + + + + + + + + + + + - -
-

- Are you sure you want to delete this session? -

-
- Cancel - Delete -
-
-
Delete diff --git a/package-lock.json b/package-lock.json index 4acd9dd..bd5f220 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "marked": "^17.0.4", "nodemailer": "^7.0.11", "nuxt": "4.4.2", + "uuid": "^14.0.0", "vue": "^3.5.25", "vue-router": "^4.6.4", "vue-signature-pad": "^3.0.2", @@ -13671,6 +13672,19 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/valibot": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", diff --git a/package.json b/package.json index 8174137..f75cfbf 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "marked": "^17.0.4", "nodemailer": "^7.0.11", "nuxt": "4.4.2", + "uuid": "^14.0.0", "vue": "^3.5.25", "vue-router": "^4.6.4", "vue-signature-pad": "^3.0.2", diff --git a/prisma/schema/appointments.prisma b/prisma/schema/appointments.prisma index d67e001..63944ca 100644 --- a/prisma/schema/appointments.prisma +++ b/prisma/schema/appointments.prisma @@ -9,6 +9,10 @@ model Appointment { status String @default("SCHEDULED") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + + seriesId String? + recurrence String + client User @relation("ClientAppointments", fields: [clientId], references: [id], onDelete: Restrict) admin User @relation("AdminAppointments", fields: [adminId], references: [id], onDelete: Restrict) diff --git a/server/api/appointments/[id].delete.ts b/server/api/appointments/[id].delete.ts index ece1cdc..9261da7 100644 --- a/server/api/appointments/[id].delete.ts +++ b/server/api/appointments/[id].delete.ts @@ -1,54 +1,90 @@ +// import { requireAdmin } from '../../utils/guard' +// import { prisma } from '../../utils/prisma' +// import { createError, defineEventHandler, getHeaders, getRouterParam } from 'h3' + +// export default defineEventHandler(async (event) => { +// try { +// const id = getRouterParam(event, 'id') +// if (!id) { +// throw createError({ +// statusCode: 400, +// statusMessage: 'Missing appointment ID', +// }) +// } + +// const user = requireAdmin(event) +// const adminId = user.id + +// await prisma.appointment.delete({ +// where: { id }, +// }) + +// return { +// success: true, +// } +// } catch (error: any) { +// if (error?.code === 'P2025') { +// throw createError({ +// statusCode: 404, +// statusMessage: 'Appointment not found', +// }) +// } + +// if (error && typeof error === 'object' && 'statusCode' in error) { +// throw error +// } + +// console.error('Delete appointment error:', error) + +// // Check if it's a Prisma error +// if (error && typeof error === 'object') { +// const errorObj = error as Record +// console.error('Error details:', { +// message: errorObj.message, +// code: errorObj.code, +// meta: errorObj.meta, +// }) +// } + +// throw createError({ +// statusCode: 500, +// statusMessage: 'Failed to delete appointment', +// }) +// } +// }) import { requireAdmin } from '../../utils/guard' import { prisma } from '../../utils/prisma' -import { createError, defineEventHandler, getHeaders, getRouterParam } from 'h3' +import { createError, defineEventHandler, getRouterParam, readBody } from 'h3' export default defineEventHandler(async (event) => { - try { - const id = getRouterParam(event, 'id') - if (!id) { - throw createError({ - statusCode: 400, - statusMessage: 'Missing appointment ID', - }) - } - - const user = requireAdmin(event) - const adminId = user.id - - await prisma.appointment.delete({ - where: { id }, + const id = getRouterParam(event, 'id') + const { type, startTime, seriesId } = await readBody(event) + + if (!id) throw createError({ statusCode: 400 }) + + // ONE + if (type === 'ONE' || !seriesId) { + await prisma.appointment.delete({ where: { id } }) + } + + // ALL + else if (type === 'ALL') { + await prisma.appointment.deleteMany({ + where: { seriesId }, }) + } - return { - success: true, - } - } catch (error: any) { - if (error?.code === 'P2025') { - throw createError({ - statusCode: 404, - statusMessage: 'Appointment not found', - }) - } - - if (error && typeof error === 'object' && 'statusCode' in error) { - throw error - } - - console.error('Delete appointment error:', error) - - // Check if it's a Prisma error - if (error && typeof error === 'object') { - const errorObj = error as Record - console.error('Error details:', { - message: errorObj.message, - code: errorObj.code, - meta: errorObj.meta, - }) - } - - throw createError({ - statusCode: 500, - statusMessage: 'Failed to delete appointment', + // FUTURE + else if (type === 'FUTURE') { + await prisma.appointment.deleteMany({ + where: { + seriesId, + startTime: { + gte: new Date(startTime), + }, + }, }) } + + return { success: true } }) diff --git a/server/api/appointments/[id].put.ts b/server/api/appointments/[id].put.ts index eb1cbe0..9a338d3 100644 --- a/server/api/appointments/[id].put.ts +++ b/server/api/appointments/[id].put.ts @@ -3,43 +3,39 @@ import { requireAdmin } from '../../utils/guard' import { defineEventHandler, getRouterParam, readBody, createError } from 'h3' export default defineEventHandler(async (event) => { - requireAdmin(event) + const id = getRouterParam(event, 'id') + const { type, startTime, seriesId, title, description, date, startTimeNew, endTimeNew } = + await readBody(event) - try { - const id = getRouterParam(event, 'id') - if (!id) throw createError({ statusCode: 400, statusMessage: 'Missing ID' }) - const { title, description, date, startTime, endTime } = await readBody(event) - - const startTimeDate = new Date(`${date}T${startTime}`) - const endTimeDate = new Date(`${date}T${endTime}`) + const newStart = new Date(`${date}T${startTimeNew}`) + const newEnd = new Date(`${date}T${endTimeNew}`) + // ONE + if (type === 'ONE' || !seriesId) { await prisma.appointment.update({ where: { id }, - data: { - title, - description, - startTime: startTimeDate, - endTime: endTimeDate, - }, + data: { title, description, startTime: newStart, endTime: newEnd }, }) + } - return { success: true } - } catch (error: any) { - if (error?.code === 'P2025') { - throw createError({ - statusCode: 404, - statusMessage: 'Appointment not found', - }) - } - - if (error && typeof error === 'object' && 'statusCode' in error) { - throw error - } + // ALL + else if (type === 'ALL') { + await prisma.appointment.updateMany({ + where: { seriesId }, + data: { title, description }, + }) + } - console.error('Error updating appointment:', error) - throw createError({ - statusCode: 500, - statusMessage: 'Failed to update appointment', + // FUTURE + else if (type === 'FUTURE') { + await prisma.appointment.updateMany({ + where: { + seriesId, + startTime: { gte: new Date(startTime) }, + }, + data: { title, description }, }) } + + return { success: true } }) diff --git a/server/api/appointments/index.get.ts b/server/api/appointments/index.get.ts index 852e60e..43c0b60 100644 --- a/server/api/appointments/index.get.ts +++ b/server/api/appointments/index.get.ts @@ -60,5 +60,6 @@ export default defineEventHandler(async (event) => { clientName: a.client.name, description: a.description, status: a.status, + seriesId: a.seriesId, })) }) diff --git a/server/api/appointments/index.post.ts b/server/api/appointments/index.post.ts index 4b2aae7..787a9ed 100644 --- a/server/api/appointments/index.post.ts +++ b/server/api/appointments/index.post.ts @@ -1,66 +1,144 @@ +// import { requireAdmin } from '../../utils/guard' +// import { prisma } from '../../utils/prisma' +// import { readBody, createError, defineEventHandler, getHeaders } from 'h3' + +// export default defineEventHandler(async (event) => { +// try { +// const body = await readBody(event) + +// const user = requireAdmin(event) +// const adminId = user.id + +// const { clientId, title, description, date, startTime, endTime } = body + +// if (!clientId || !title || !date || !startTime || !endTime) { +// throw createError({ +// statusCode: 400, +// statusMessage: 'Missing required fields', +// }) +// } + +// const start = new Date(`${date}T${startTime}`) +// const end = new Date(`${date}T${endTime}`) + +// if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || end <= start) { +// throw createError({ +// statusCode: 400, +// statusMessage: 'Invalid date/time range', +// }) +// } + +// const appointment = await prisma.appointment.create({ +// data: { +// clientId, +// adminId, +// title, +// description, +// startTime: start, +// endTime: end, +// status: 'SCHEDULED', +// }, +// }) + +// return { +// success: true, +// appointment, +// } +// } catch (error: any) { +// if (error && typeof error === 'object' && 'statusCode' in error) { +// throw error +// } + +// if (error?.code === 'P2003') { +// throw createError({ +// statusCode: 400, +// statusMessage: 'Invalid client ID or Foreign Key constraint failed', +// }) +// } + +// console.error('Create appointment error:', error) + +// throw createError({ +// statusCode: 500, +// statusMessage: 'Failed to create appointment', +// }) +// } +// }) import { requireAdmin } from '../../utils/guard' import { prisma } from '../../utils/prisma' -import { readBody, createError, defineEventHandler, getHeaders } from 'h3' +import { readBody, createError, defineEventHandler } from 'h3' +import { v4 as uuidv4 } from 'uuid' export default defineEventHandler(async (event) => { try { const body = await readBody(event) - const user = requireAdmin(event) const adminId = user.id - const { clientId, title, description, date, startTime, endTime } = body + const { clientId, title, description, date, startTime, endTime, isRecurring, recurrence } = body if (!clientId || !title || !date || !startTime || !endTime) { - throw createError({ - statusCode: 400, - statusMessage: 'Missing required fields', - }) + throw createError({ statusCode: 400, statusMessage: 'Missing required fields' }) } - const start = new Date(`${date}T${startTime}`) - const end = new Date(`${date}T${endTime}`) + const baseStart = new Date(`${date}T${startTime}`) + const baseEnd = new Date(`${date}T${endTime}`) - if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || end <= start) { - throw createError({ - statusCode: 400, - statusMessage: 'Invalid date/time range', - }) + if (baseEnd <= baseStart) { + throw createError({ statusCode: 400, statusMessage: 'Invalid time range' }) } - const appointment = await prisma.appointment.create({ - data: { + const seriesId = isRecurring ? uuidv4() : null + const count = isRecurring ? 20 : 1 + + const appointmentsToCreate = [] + + for (let i = 0; i < count; i++) { + const newStart = new Date(baseStart) + const newEnd = new Date(baseEnd) + + if (isRecurring) { + if (recurrence === 'DAILY') { + newStart.setDate(baseStart.getDate() + i) + newEnd.setDate(baseEnd.getDate() + i) + } + + if (recurrence === 'WEEKLY') { + newStart.setDate(baseStart.getDate() + i * 7) + newEnd.setDate(baseEnd.getDate() + i * 7) + } + + if (recurrence === 'MONTHLY') { + newStart.setMonth(baseStart.getMonth() + i) + newEnd.setMonth(baseEnd.getMonth() + i) + } + } + + appointmentsToCreate.push({ clientId, adminId, title, description, - startTime: start, - endTime: end, + startTime: newStart, + endTime: newEnd, status: 'SCHEDULED', - }, - }) - - return { - success: true, - appointment, - } - } catch (error: any) { - if (error && typeof error === 'object' && 'statusCode' in error) { - throw error - } - - if (error?.code === 'P2003') { - throw createError({ - statusCode: 400, - statusMessage: 'Invalid client ID or Foreign Key constraint failed', + seriesId, + recurrence: isRecurring ? recurrence : null, }) } - console.error('Create appointment error:', error) + await prisma.appointment.createMany({ + data: appointmentsToCreate, + }) + + return { success: true } + } catch (error: any) { + console.error('🔥 BACKEND FULL ERROR:', error) + console.error('🔥 BACKEND STACK:', error?.stack) throw createError({ statusCode: 500, - statusMessage: 'Failed to create appointment', + statusMessage: error?.message || JSON.stringify(error), }) } }) From 478ecc0f9490d0489d29ae16ab1735409ef00a59 Mon Sep 17 00:00:00 2001 From: ritikhaashok Date: Thu, 23 Apr 2026 15:52:50 -0500 Subject: [PATCH 3/4] deleted commented code --- server/api/appointments/[id].delete.ts | 54 --------------------- server/api/appointments/index.post.ts | 66 -------------------------- 2 files changed, 120 deletions(-) diff --git a/server/api/appointments/[id].delete.ts b/server/api/appointments/[id].delete.ts index 9261da7..546ad01 100644 --- a/server/api/appointments/[id].delete.ts +++ b/server/api/appointments/[id].delete.ts @@ -1,57 +1,3 @@ -// import { requireAdmin } from '../../utils/guard' -// import { prisma } from '../../utils/prisma' -// import { createError, defineEventHandler, getHeaders, getRouterParam } from 'h3' - -// export default defineEventHandler(async (event) => { -// try { -// const id = getRouterParam(event, 'id') -// if (!id) { -// throw createError({ -// statusCode: 400, -// statusMessage: 'Missing appointment ID', -// }) -// } - -// const user = requireAdmin(event) -// const adminId = user.id - -// await prisma.appointment.delete({ -// where: { id }, -// }) - -// return { -// success: true, -// } -// } catch (error: any) { -// if (error?.code === 'P2025') { -// throw createError({ -// statusCode: 404, -// statusMessage: 'Appointment not found', -// }) -// } - -// if (error && typeof error === 'object' && 'statusCode' in error) { -// throw error -// } - -// console.error('Delete appointment error:', error) - -// // Check if it's a Prisma error -// if (error && typeof error === 'object') { -// const errorObj = error as Record -// console.error('Error details:', { -// message: errorObj.message, -// code: errorObj.code, -// meta: errorObj.meta, -// }) -// } - -// throw createError({ -// statusCode: 500, -// statusMessage: 'Failed to delete appointment', -// }) -// } -// }) import { requireAdmin } from '../../utils/guard' import { prisma } from '../../utils/prisma' import { createError, defineEventHandler, getRouterParam, readBody } from 'h3' diff --git a/server/api/appointments/index.post.ts b/server/api/appointments/index.post.ts index 787a9ed..67e204f 100644 --- a/server/api/appointments/index.post.ts +++ b/server/api/appointments/index.post.ts @@ -1,69 +1,3 @@ -// import { requireAdmin } from '../../utils/guard' -// import { prisma } from '../../utils/prisma' -// import { readBody, createError, defineEventHandler, getHeaders } from 'h3' - -// export default defineEventHandler(async (event) => { -// try { -// const body = await readBody(event) - -// const user = requireAdmin(event) -// const adminId = user.id - -// const { clientId, title, description, date, startTime, endTime } = body - -// if (!clientId || !title || !date || !startTime || !endTime) { -// throw createError({ -// statusCode: 400, -// statusMessage: 'Missing required fields', -// }) -// } - -// const start = new Date(`${date}T${startTime}`) -// const end = new Date(`${date}T${endTime}`) - -// if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || end <= start) { -// throw createError({ -// statusCode: 400, -// statusMessage: 'Invalid date/time range', -// }) -// } - -// const appointment = await prisma.appointment.create({ -// data: { -// clientId, -// adminId, -// title, -// description, -// startTime: start, -// endTime: end, -// status: 'SCHEDULED', -// }, -// }) - -// return { -// success: true, -// appointment, -// } -// } catch (error: any) { -// if (error && typeof error === 'object' && 'statusCode' in error) { -// throw error -// } - -// if (error?.code === 'P2003') { -// throw createError({ -// statusCode: 400, -// statusMessage: 'Invalid client ID or Foreign Key constraint failed', -// }) -// } - -// console.error('Create appointment error:', error) - -// throw createError({ -// statusCode: 500, -// statusMessage: 'Failed to create appointment', -// }) -// } -// }) import { requireAdmin } from '../../utils/guard' import { prisma } from '../../utils/prisma' import { readBody, createError, defineEventHandler } from 'h3' From 44427446b75a4eecbfd856e07733352e75c4912c Mon Sep 17 00:00:00 2001 From: ritikhaashok Date: Fri, 24 Apr 2026 13:01:08 -0500 Subject: [PATCH 4/4] updated interaction for switching between days, weeks, and months --- app/pages/calendar.vue | 46 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/app/pages/calendar.vue b/app/pages/calendar.vue index bebfe86..cd7dbcb 100644 --- a/app/pages/calendar.vue +++ b/app/pages/calendar.vue @@ -172,6 +172,32 @@ const isEditMode = ref(false) const isDeleteConfirming = ref(false) const mobileView = ref('week') + const currentRangeLabel = ref('') + + function formatShortDate(date: Date) { + return date.toLocaleDateString('en-US') + } + + function updateRangeLabel(viewType: string, start: Date) { + if (viewType.includes('Week')) { + currentRangeLabel.value = `Week of ${formatShortDate(start)}` + return + } + + if (viewType.includes('Month')) { + currentRangeLabel.value = start.toLocaleDateString('en-US', { + month: 'long', + year: 'numeric', + }) + return + } + + currentRangeLabel.value = start.toLocaleDateString('en-US', { + month: 'long', + day: 'numeric', + year: 'numeric', + }) + } watch(mobileView, (view) => { changeView(view) @@ -202,6 +228,7 @@ datesSet(info) { const view = info.view.type + updateRangeLabel(view, info.view.currentStart) if (view.includes('Day')) mobileView.value = 'day' if (view.includes('Week')) mobileView.value = 'week' @@ -485,15 +512,22 @@ +

+ {{ currentRangeLabel }} +

- - +
+ + +