diff --git a/drizzle/0004_motionless_havok.sql b/drizzle/0004_motionless_havok.sql new file mode 100644 index 0000000..975a87b --- /dev/null +++ b/drizzle/0004_motionless_havok.sql @@ -0,0 +1,11 @@ +CREATE TABLE `survey_comments_table` ( + `id` int AUTO_INCREMENT NOT NULL, + `surveyId` int NOT NULL, + `participantId` int NOT NULL, + `comment` text NOT NULL, + CONSTRAINT `survey_comments_table_id` PRIMARY KEY(`id`) +); +--> statement-breakpoint +ALTER TABLE `survey_comments_table` ADD CONSTRAINT `survey_comments_table_surveyId_surveys_table_id_fk` FOREIGN KEY (`surveyId`) REFERENCES `surveys_table`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE `survey_comments_table` ADD CONSTRAINT `survey_comments_table_participantId_survey_access_table_id_fk` FOREIGN KEY (`participantId`) REFERENCES `survey_access_table`(`id`) ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +CREATE INDEX `survey_participant_index` ON `survey_comments_table` (`surveyId`,`participantId`); \ No newline at end of file diff --git a/drizzle/meta/0004_snapshot.json b/drizzle/meta/0004_snapshot.json new file mode 100644 index 0000000..7e675fe --- /dev/null +++ b/drizzle/meta/0004_snapshot.json @@ -0,0 +1,556 @@ +{ + "version": "5", + "dialect": "mysql", + "id": "77ad797c-05e7-432e-bb86-b6a0bd22f71a", + "prevId": "4d29fb1a-427c-4082-a176-08688a6d9f2e", + "tables": { + "sessions": { + "name": "sessions", + "columns": { + "token": { + "name": "token", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "bigint", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "sessions_userId_users_table_id_fk": { + "name": "sessions_userId_users_table_id_fk", + "tableFrom": "sessions", + "tableTo": "users_table", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "sessions_token": { + "name": "sessions_token", + "columns": [ + "token" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "survey_access_table": { + "name": "survey_access_table", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "surveyId": { + "name": "surveyId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recepientEmail": { + "name": "recepientEmail", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accessToken": { + "name": "accessToken", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "token_index": { + "name": "token_index", + "columns": [ + "accessToken" + ], + "isUnique": false + } + }, + "foreignKeys": { + "survey_access_table_surveyId_surveys_table_id_fk": { + "name": "survey_access_table_surveyId_surveys_table_id_fk", + "tableFrom": "survey_access_table", + "tableTo": "surveys_table", + "columnsFrom": [ + "surveyId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "survey_access_table_id": { + "name": "survey_access_table_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "survey_answers_table": { + "name": "survey_answers_table", + "columns": { + "participantId": { + "name": "participantId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "skillId": { + "name": "skillId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "rating": { + "name": "rating", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "survey_answers_table_participantId_survey_access_table_id_fk": { + "name": "survey_answers_table_participantId_survey_access_table_id_fk", + "tableFrom": "survey_answers_table", + "tableTo": "survey_access_table", + "columnsFrom": [ + "participantId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "survey_answers_table_skillId_survey_skills_table_id_fk": { + "name": "survey_answers_table_skillId_survey_skills_table_id_fk", + "tableFrom": "survey_answers_table", + "tableTo": "survey_skills_table", + "columnsFrom": [ + "skillId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "survey_answers_table_participantId_skillId_pk": { + "name": "survey_answers_table_participantId_skillId_pk", + "columns": [ + "participantId", + "skillId" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "survey_comments_table": { + "name": "survey_comments_table", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "surveyId": { + "name": "surveyId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "participantId": { + "name": "participantId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "comment": { + "name": "comment", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "survey_participant_index": { + "name": "survey_participant_index", + "columns": [ + "surveyId", + "participantId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "survey_comments_table_surveyId_surveys_table_id_fk": { + "name": "survey_comments_table_surveyId_surveys_table_id_fk", + "tableFrom": "survey_comments_table", + "tableTo": "surveys_table", + "columnsFrom": [ + "surveyId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "survey_comments_table_participantId_survey_access_table_id_fk": { + "name": "survey_comments_table_participantId_survey_access_table_id_fk", + "tableFrom": "survey_comments_table", + "tableTo": "survey_access_table", + "columnsFrom": [ + "participantId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "survey_comments_table_id": { + "name": "survey_comments_table_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "survey_permissions_table": { + "name": "survey_permissions_table", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "surveyId": { + "name": "surveyId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user": { + "name": "user", + "type": "int", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access": { + "name": "access", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "user_index": { + "name": "user_index", + "columns": [ + "user" + ], + "isUnique": false + }, + "survey_index": { + "name": "survey_index", + "columns": [ + "surveyId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "survey_permissions_table_surveyId_surveys_table_id_fk": { + "name": "survey_permissions_table_surveyId_surveys_table_id_fk", + "tableFrom": "survey_permissions_table", + "tableTo": "surveys_table", + "columnsFrom": [ + "surveyId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "survey_permissions_table_user_users_table_id_fk": { + "name": "survey_permissions_table_user_users_table_id_fk", + "tableFrom": "survey_permissions_table", + "tableTo": "users_table", + "columnsFrom": [ + "user" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "survey_permissions_table_id": { + "name": "survey_permissions_table_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "survey_user_un": { + "name": "survey_user_un", + "columns": [ + "surveyId", + "user" + ] + } + }, + "checkConstraint": {} + }, + "survey_skills_table": { + "name": "survey_skills_table", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "surveyId": { + "name": "surveyId", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "survey_skills_table_surveyId_surveys_table_id_fk": { + "name": "survey_skills_table_surveyId_surveys_table_id_fk", + "tableFrom": "survey_skills_table", + "tableTo": "surveys_table", + "columnsFrom": [ + "surveyId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "survey_skills_table_id": { + "name": "survey_skills_table_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "surveys_table": { + "name": "surveys_table", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "surveys_table_owner_users_table_id_fk": { + "name": "surveys_table_owner_users_table_id_fk", + "tableFrom": "surveys_table", + "tableTo": "users_table", + "columnsFrom": [ + "owner" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "surveys_table_id": { + "name": "surveys_table_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": {}, + "checkConstraint": {} + }, + "users_table": { + "name": "users_table", + "columns": { + "id": { + "name": "id", + "type": "int", + "primaryKey": false, + "notNull": true, + "autoincrement": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "verification_code": { + "name": "verification_code", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verifcationCodeExpires": { + "name": "verifcationCodeExpires", + "type": "date", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "users_table_id": { + "name": "users_table_id", + "columns": [ + "id" + ] + } + }, + "uniqueConstraints": { + "users_table_email_unique": { + "name": "users_table_email_unique", + "columns": [ + "email" + ] + } + }, + "checkConstraint": {} + } + }, + "views": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "tables": {}, + "indexes": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index c9fb824..45d1086 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -29,6 +29,13 @@ "when": 1736628358111, "tag": "0003_overjoyed_shooting_star", "breakpoints": true + }, + { + "idx": 4, + "version": "5", + "when": 1745216339779, + "tag": "0004_motionless_havok", + "breakpoints": true } ] } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4c8ebee..c5f15ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "d3-shape": "^3.2.0", "debug": "^4.4.0", "drizzle-orm": "^0.36.4", - "layerchart": "^0.59.1", + "layerchart": "^1.0.8", "markdown-it": "^14.1.0", "mysql2": "^3.11.4", "nodemailer": "^6.9.16" @@ -81,6 +81,24 @@ "node": ">=6.0.0" } }, + "node_modules/@dagrejs/dagre": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.4.tgz", + "integrity": "sha512-QUTc54Cg/wvmlEUxB+uvoPVKFazM1H18kVHBQNmK2NbrDR5ihOCR6CXLnDSZzMcSQKJtabPUWridBOlJM3WkDg==", + "license": "MIT", + "dependencies": { + "@dagrejs/graphlib": "2.2.4" + } + }, + "node_modules/@dagrejs/graphlib": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.4.tgz", + "integrity": "sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==", + "license": "MIT", + "engines": { + "node": ">17.0.0" + } + }, "node_modules/@drizzle-team/brocli": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@drizzle-team/brocli/-/brocli-0.10.2.tgz", @@ -1096,19 +1114,19 @@ } }, "node_modules/@floating-ui/dom": { - "version": "1.6.12", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", - "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", "license": "MIT", "dependencies": { "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.8" + "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", - "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", "license": "MIT" }, "node_modules/@humanfs/core": { @@ -1253,13 +1271,13 @@ } }, "node_modules/@layerstack/svelte-actions": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@layerstack/svelte-actions/-/svelte-actions-0.0.9.tgz", - "integrity": "sha512-1mDXM7R0pT3pvQvXS479hN9tkn6M812Iip9M7BSorDTQcccGNVGU67aqLO5qded7CuzRnyeN2id5KcVU5zfY8A==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@layerstack/svelte-actions/-/svelte-actions-1.0.0.tgz", + "integrity": "sha512-cnVxMbcc+xYta9hwHnLja4a7bR7cfX/QW9jDY4ZCI8RIt+3lKNRE+u2XzWoGp4Qsx7A0/nGUIiLWpDBgO1ihsQ==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.6.12", - "@layerstack/utils": "0.0.7", + "@floating-ui/dom": "^1.6.13", + "@layerstack/utils": "1.0.0", "d3-array": "^3.2.4", "d3-scale": "^4.0.2", "date-fns": "^4.1.0", @@ -1267,26 +1285,26 @@ } }, "node_modules/@layerstack/svelte-stores": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/@layerstack/svelte-stores/-/svelte-stores-0.0.9.tgz", - "integrity": "sha512-5yyi7eV/2hOraw7wQgEObVR4s2/4T3G/GjGaZLR9BQH8mnsLuXw1n3k6rFT8NxiRvpEjWJ9l9ILOGc/XzFJb3g==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@layerstack/svelte-stores/-/svelte-stores-1.0.1.tgz", + "integrity": "sha512-GVm0iMNJ8pI2q8WjofwejK+m6uF7PFUqgZZfkgDv2UmVh/HyW6ZpxA2wXzIxZley4NtUlrRw8cpyy2sJOEGVSA==", "license": "MIT", "dependencies": { - "@layerstack/utils": "0.0.7", + "@layerstack/utils": "1.0.0", "d3-array": "^3.2.4", "date-fns": "^4.1.0", "immer": "^10.1.1", "lodash-es": "^4.17.21", - "zod": "^3.23.8" + "zod": "^3.24.2" } }, "node_modules/@layerstack/tailwind": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@layerstack/tailwind/-/tailwind-0.0.11.tgz", - "integrity": "sha512-dHvJM5xpVisPrM6NClSCNPqB/u+pSwemWzkKeb911/8pLUuyr+7HceTpv5AZMyE/I5A2RxoTa5mTZokmvEqPnQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@layerstack/tailwind/-/tailwind-1.0.0.tgz", + "integrity": "sha512-xV2M6KRNBmbhV9XAvTHiw/BJSTIh5eby+Hj1iXXkMr88fObZHn4s1G2BILEDbdwjZl6Ivu1Du0gO9Vz3lzflUA==", "license": "MIT", "dependencies": { - "@layerstack/utils": "^0.0.7", + "@layerstack/utils": "^1.0.0", "clsx": "^2.1.1", "culori": "^4.0.1", "d3-array": "^3.2.4", @@ -1297,9 +1315,9 @@ } }, "node_modules/@layerstack/utils": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@layerstack/utils/-/utils-0.0.7.tgz", - "integrity": "sha512-7R9AOw/J3TkvSCuQXajK28d7AKuvsEgMQlXBSvAr9tcxSrb9hzraX47uD4uEmEAIB9IiKaG4IUx2FmGiJ3WlTQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@layerstack/utils/-/utils-1.0.0.tgz", + "integrity": "sha512-kIH8lo4MEzPGtdxQRacy8RsSVhMQwe62ckuev62xQDE5QcUJ4wB9EMaRWaQK8CfEOpUERGPgkMPMbMqPhdp0gw==", "license": "MIT", "dependencies": { "d3-array": "^3.2.4", @@ -2865,12 +2883,6 @@ "node": ">= 0.4" } }, - "node_modules/array-source": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/array-source/-/array-source-0.0.4.tgz", - "integrity": "sha512-frNdc+zBn80vipY+GdcJkLEbMWj3xmzArYApmUGxoiV8uAu/ygcs9icPdsGdA26h0MkHUMW6EN2piIvVx+M5Mw==", - "license": "BSD-3-Clause" - }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -3141,17 +3153,6 @@ "node": ">= 0.6" } }, - "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4197,12 +4198,6 @@ "node": "^12.20 || >= 14.13" } }, - "node_modules/fflate": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", - "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", - "license": "MIT" - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -4216,15 +4211,6 @@ "node": ">=16.0.0" } }, - "node_modules/file-source": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/file-source/-/file-source-0.6.1.tgz", - "integrity": "sha512-1R1KneL7eTXmXfKxC10V/9NeGOdbsAXJ+lQ//fvvcHUgtaZcZDWNJNblxAoVOyV1cj45pOtUrR3vZTBwqcW8XA==", - "license": "BSD-3-Clause", - "dependencies": { - "stream-source": "0.3" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4766,15 +4752,16 @@ } }, "node_modules/layerchart": { - "version": "0.59.6", - "resolved": "https://registry.npmjs.org/layerchart/-/layerchart-0.59.6.tgz", - "integrity": "sha512-P7ed+YDykJnoMFqDh/npE8SJWIiMvoro85sJx+hglEYmOro0JldW78BRXl1UNNh04W42OS+Ky/kGTTPI0Pm22Q==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/layerchart/-/layerchart-1.0.8.tgz", + "integrity": "sha512-JkSNLUFiTkUYOmJclnM97oO/xtr/YuF4bnAf1ySz2ZeoS97HNUgWjIcYl5JkNrF112VA1K/ODranx5ZbYpXxqA==", "license": "MIT", "dependencies": { - "@layerstack/svelte-actions": "^0.0.9", - "@layerstack/svelte-stores": "^0.0.9", - "@layerstack/tailwind": "^0.0.11", - "@layerstack/utils": "^0.0.7", + "@dagrejs/dagre": "^1.1.4", + "@layerstack/svelte-actions": "^1.0.0", + "@layerstack/svelte-stores": "^1.0.0", + "@layerstack/tailwind": "^1.0.0", + "@layerstack/utils": "^1.0.0", "d3-array": "^3.2.4", "d3-color": "^3.1.0", "d3-delaunay": "^6.0.4", @@ -4796,10 +4783,7 @@ "d3-time": "^3.1.0", "date-fns": "^4.1.0", "layercake": "^8.4.2", - "lodash-es": "^4.17.21", - "posthog-js": "^1.187.2", - "shapefile": "^0.6.6", - "topojson-client": "^3.1.0" + "lodash-es": "^4.17.21" }, "peerDependencies": { "svelte": "^3.56.0 || ^4.0.0 || ^5.0.0" @@ -5346,16 +5330,6 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, - "node_modules/path-source": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/path-source/-/path-source-0.1.3.tgz", - "integrity": "sha512-dWRHm5mIw5kw0cs3QZLNmpUWty48f5+5v9nWD2dw3Y0Hf+s01Ag8iJEWV0Sm0kocE8kK27DrIowha03e1YR+Qw==", - "license": "BSD-3-Clause", - "dependencies": { - "array-source": "0.0", - "file-source": "0.6" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5588,28 +5562,6 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, - "node_modules/posthog-js": { - "version": "1.203.1", - "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.203.1.tgz", - "integrity": "sha512-r/WiSyz6VNbIKEV/30+aD5gdrYkFtmZwvqNa6h9frl8hG638v098FrXaq3EYzMcCdkQf3phaZTDIAFKegpiTjw==", - "license": "MIT", - "dependencies": { - "core-js": "^3.38.1", - "fflate": "^0.4.8", - "preact": "^10.19.3", - "web-vitals": "^4.2.0" - } - }, - "node_modules/preact": { - "version": "10.25.3", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.25.3.tgz", - "integrity": "sha512-dzQmIFtM970z+fP9ziQ3yG4e3ULIbwZzJ734vaMVUTaKQ2+Ru1Ou/gjshOYVHCcd1rpAelC6ngjvjDXph98unQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/preact" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5962,30 +5914,6 @@ "dev": true, "license": "MIT" }, - "node_modules/shapefile": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/shapefile/-/shapefile-0.6.6.tgz", - "integrity": "sha512-rLGSWeK2ufzCVx05wYd+xrWnOOdSV7xNUW5/XFgx3Bc02hBkpMlrd2F1dDII7/jhWzv0MSyBFh5uJIy9hLdfuw==", - "license": "BSD-3-Clause", - "dependencies": { - "array-source": "0.0", - "commander": "2", - "path-source": "0.1", - "slice-source": "0.4", - "stream-source": "0.3", - "text-encoding": "^0.6.4" - }, - "bin": { - "dbf2json": "bin/dbf2json", - "shp2json": "bin/shp2json" - } - }, - "node_modules/shapefile/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6034,12 +5962,6 @@ "node": ">=18" } }, - "node_modules/slice-source": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/slice-source/-/slice-source-0.4.1.tgz", - "integrity": "sha512-YiuPbxpCj4hD9Qs06hGAz/OZhQ0eDuALN0lRWJez0eD/RevzKqGdUx1IOMUnXgpr+sXZLq3g8ERwbAH0bCb8vg==", - "license": "BSD-3-Clause" - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6079,12 +6001,6 @@ "node": ">= 0.6" } }, - "node_modules/stream-source": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/stream-source/-/stream-source-0.3.5.tgz", - "integrity": "sha512-ZuEDP9sgjiAwUVoDModftG0JtYiLUV8K4ljYD1VyUMRWtbVf92474o4kuuul43iZ8t/hRuiDAx1dIJSvirrK/g==", - "license": "BSD-3-Clause" - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -6569,13 +6485,6 @@ "node": ">= 14" } }, - "node_modules/text-encoding": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", - "integrity": "sha512-hJnc6Qg3dWoOMkqP53F0dzRIgtmsAge09kxUIqGrEUS4qr5rWLckGYaQAVr+opBrIMRErGgy6f5aPnyPpyGRfg==", - "deprecated": "no longer maintained", - "license": "Unlicense" - }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -6620,26 +6529,6 @@ "node": ">=8.0" } }, - "node_modules/topojson-client": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-3.1.0.tgz", - "integrity": "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw==", - "license": "ISC", - "dependencies": { - "commander": "2" - }, - "bin": { - "topo2geo": "bin/topo2geo", - "topomerge": "bin/topomerge", - "topoquantize": "bin/topoquantize" - } - }, - "node_modules/topojson-client/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT" - }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -7302,12 +7191,6 @@ "node": ">= 8" } }, - "node_modules/web-vitals": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", - "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==", - "license": "Apache-2.0" - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7472,9 +7355,9 @@ "license": "MIT" }, "node_modules/zod": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", - "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", + "version": "3.24.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 1058af0..7051ee7 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "d3-shape": "^3.2.0", "debug": "^4.4.0", "drizzle-orm": "^0.36.4", - "layerchart": "^0.59.1", + "layerchart": "^1.0.8", "markdown-it": "^14.1.0", "mysql2": "^3.11.4", "nodemailer": "^6.9.16" diff --git a/src/db/schema.ts b/src/db/schema.ts index 07b0d30..537ecb7 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -45,6 +45,15 @@ export const surveyAnswersTable = mysqlTable("survey_answers_table", { pk: primaryKey({ columns: [table.participantId, table.skillId] }), })); +export const surveyCommentsTable = mysqlTable("survey_comments_table", { + id: int().autoincrement().primaryKey(), + surveyId: int().notNull().references(() => surveysTable.id, { onDelete: "cascade" }), + participantId: int().notNull().references(() => surveyAccessTable.id, { onDelete: "cascade" }), + comment: text().notNull(), +}, (table) => ({ + surveyParticipantIndex: index("survey_participant_index").on(table.surveyId, table.participantId), +})); + export const surveyPermissionsTable = mysqlTable("survey_permissions_table", { id: int().autoincrement().primaryKey(), surveyId: int().notNull().references(() => surveysTable.id, { onDelete: "cascade" }), diff --git a/src/db/survey.ts b/src/db/survey.ts index ec4247e..4ca2820 100644 --- a/src/db/survey.ts +++ b/src/db/survey.ts @@ -5,7 +5,7 @@ const log = debug('survey:admin:edit'); import { eq, or, and, inArray, gte, sql, isNull } from "drizzle-orm"; import { db } from "../db"; -import { surveyAccessTable, surveyAnswersTable, surveyPermissionsTable, surveySkillsTable, surveysTable, usersTable } from "../db/schema"; +import { surveyAccessTable, surveyAnswersTable, surveyCommentsTable, surveyPermissionsTable, surveySkillsTable, surveysTable, usersTable } from "../db/schema"; import { AccessLevel, type AccessToken, type Email, type ParticipantId, type SkillId, type SurveyData, type SurveyId, type SurveyMetaData, type UserId } from "$lib/types"; import { checkAccess, whenAccess } from "$lib/helpers/shared/permissions"; import { generateRandomToken } from "$lib/helpers/shared/randomToken"; @@ -125,6 +125,7 @@ export async function loadSurvey(id: SurveyId, userId: UserId): Promise db.select().from(surveyCommentsTable).where(eq(surveyCommentsTable.surveyId, id))); return { ...survey, @@ -135,7 +136,8 @@ export async function loadSurvey(id: SurveyId, userId: UserId): Promise answer.participantId === participant.id).map(answer => ({ skillId: answer.skillId as SkillId, rating: answer.rating / 10 // convert back to original vallue. The DB stores integer values only to prevent rounding and matching errors - })) ?? [] + })) ?? [], + comment: comments?.find(comment => comment.participantId === participant.id)?.comment ?? null })) ?? [], skills: skills?.map(skill => ({ id: skill.id as SkillId, diff --git a/src/lib/components/Comments.svelte b/src/lib/components/Comments.svelte new file mode 100644 index 0000000..86df101 --- /dev/null +++ b/src/lib/components/Comments.svelte @@ -0,0 +1,21 @@ + + +
+ {#each comments.filter((participant) => !!participant.comment) as comment} +

{comment.email}:

+

{comment.comment}

+ {:else} + No comments + {/each} +
diff --git a/src/lib/types.ts b/src/lib/types.ts index 49f3149..5554cd4 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -55,6 +55,7 @@ export type SurveyParticipant = { email: Email; accessToken: AccessToken | null; answers: SurveyAnswer[]; + comment: string | null; } /// Information about a specific skill diff --git a/src/routes/(app)/survey/[surveyId]/+page.svelte b/src/routes/(app)/survey/[surveyId]/+page.svelte index 4b6829b..ac8aa5d 100644 --- a/src/routes/(app)/survey/[surveyId]/+page.svelte +++ b/src/routes/(app)/survey/[surveyId]/+page.svelte @@ -20,6 +20,7 @@ import Button from '$lib/components/Button.svelte'; import EyeSlash from '$lib/components/icons/EyeSlash.svelte'; import Eye from '$lib/components/icons/Eye.svelte'; + import Comments from '$lib/components/Comments.svelte'; let { data }: { data: PageData } = $props(); @@ -164,6 +165,8 @@
+

Comments

+ {:else}
You do not have permission to see the survey results
{/if} diff --git a/src/routes/[accessToken]/+page.server.ts b/src/routes/[accessToken]/+page.server.ts index 42a1db8..15e7cba 100644 --- a/src/routes/[accessToken]/+page.server.ts +++ b/src/routes/[accessToken]/+page.server.ts @@ -1,6 +1,6 @@ import { error, redirect } from '@sveltejs/kit'; import { db } from '../../db'; -import { surveyAccessTable, surveyAnswersTable, surveySkillsTable, surveysTable } from '../../db/schema'; +import { surveyAccessTable, surveyAnswersTable, surveyCommentsTable, surveySkillsTable, surveysTable } from '../../db/schema'; import type { PageServerLoad } from './$types'; import { eq } from 'drizzle-orm'; @@ -65,13 +65,13 @@ export const actions = { const formData = await request.formData(); // validate that the form doesn't contain invalid skill IDs - const skillEntries = [...formData.entries()].filter(([key, _]) => !key.startsWith('disable-')); + const skillEntries = [...formData.entries()].filter(([key, _]) => !key.startsWith('disable-') && key !== 'comment'); const allIdsValid = skillEntries.every(([key, _]) => skills.some(skill => skill.id.toString() === key)); const deselectedIds = [...formData.entries().filter(([key, _]) => key.startsWith('disable-')).map(([key, _]) => parseInt(key.replace('disable-', '')))]; if (!allIdsValid || skillEntries.length + deselectedIds.length !== skills.length) { let missing_ids = skills.filter(skill => !skillEntries.some(([key, _]) => key === skill.id.toString()) && !deselectedIds.includes(skill.id)); let invalid_ids = skillEntries.filter(([key, _]) => !skills.some(skill => skill.id.toString() === key)).map(([key, _]) => key); - log_store.log("Invalid (%o) or missing (%o) skill IDs", invalid_ids, missing_ids); + log_store("Invalid (%o) or missing (%o) skill IDs", invalid_ids, missing_ids); error(400, 'Invalid skill ID'); } @@ -90,6 +90,14 @@ export const actions = { await db.insert(surveyAnswersTable).values([...answers]); + const comment = formData.get('comment'); + if (comment) { + await db.insert(surveyCommentsTable).values({ + surveyId: survey.id, participantId: results[0].survey_access_table.id, + comment: comment.toString() + }); + } + redirect(303, `/${params.accessToken}/thanks`); } } \ No newline at end of file diff --git a/src/routes/[accessToken]/+page.svelte b/src/routes/[accessToken]/+page.svelte index f354004..bd21369 100644 --- a/src/routes/[accessToken]/+page.svelte +++ b/src/routes/[accessToken]/+page.svelte @@ -26,6 +26,11 @@
+ + +