From 14ffcfd5f6d5cbaecd508f222dd751a6e6617880 Mon Sep 17 00:00:00 2001 From: Gisle Aune Date: Sat, 26 Mar 2022 14:53:12 +0100 Subject: [PATCH] add a bunch of stuff. --- package-lock.json | 189 +++++++++++++++++- package.json | 1 + scripts/goose-mysql/20220313115117_scope.sql | 13 ++ .../20220313115122_scope_member.sql | 17 ++ src/app.d.ts | 7 + src/app.html | 2 +- src/hooks.ts | 25 +++ src/lib/components/colors.sass | 4 + .../frontpage/RequirementLink.svelte | 2 +- src/lib/components/layout/Backgrond.svelte | 64 ++++++ src/lib/components/layout/Entry.svelte | 5 +- .../components/layout/EntryStatusLine.svelte | 43 +--- src/lib/components/layout/Header.svelte | 39 ++++ src/lib/components/layout/Icon.svelte | 4 + src/lib/components/layout/MenuCategory.svelte | 67 +++++++ src/lib/components/layout/MenuItem.svelte | 77 +++++++ src/lib/components/layout/StatusIcon.svelte | 21 ++ src/lib/components/layout/StatusText.svelte | 19 ++ src/lib/config.ts | 18 ++ src/lib/database/interfaces.ts | 31 +++ src/lib/database/mysql/database.ts | 48 +++++ src/lib/database/mysql/scopes.ts | 43 ++++ src/lib/models/scope.ts | 12 ++ src/lib/models/status.ts | 16 ++ src/lib/models/user.ts | 4 + src/routes/[scope].json.ts | 15 +- src/routes/[scope]/__layout.svelte | 82 +++++++- src/routes/[scope]/index.svelte | 8 +- src/routes/__layout.svelte | 10 + src/routes/index.svelte | 30 +-- src/routes/indexdata.json.ts | 10 +- static/background.jpg | Bin 0 -> 507024 bytes 32 files changed, 837 insertions(+), 89 deletions(-) create mode 100644 scripts/goose-mysql/20220313115117_scope.sql create mode 100644 scripts/goose-mysql/20220313115122_scope_member.sql create mode 100644 src/hooks.ts create mode 100644 src/lib/components/layout/Backgrond.svelte create mode 100644 src/lib/components/layout/Header.svelte create mode 100644 src/lib/components/layout/MenuCategory.svelte create mode 100644 src/lib/components/layout/MenuItem.svelte create mode 100644 src/lib/components/layout/StatusIcon.svelte create mode 100644 src/lib/components/layout/StatusText.svelte create mode 100644 src/lib/config.ts create mode 100644 src/lib/database/interfaces.ts create mode 100644 src/lib/database/mysql/database.ts create mode 100644 src/lib/database/mysql/scopes.ts create mode 100644 src/lib/models/user.ts create mode 100644 src/routes/__layout.svelte create mode 100644 static/background.jpg diff --git a/package-lock.json b/package-lock.json index 5495afb..c9e306f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "eslint": "^7.32.0", "eslint-plugin-svelte3": "^3.2.1", "fa-svelte": "^3.1.0", + "mysql2": "^2.3.3", "node-sass": "^7.0.1", "sass": "^1.49.9", "svelte": "^3.44.0", @@ -1225,6 +1226,15 @@ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true }, + "node_modules/denque": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -2176,6 +2186,15 @@ "node": ">= 4.0.0" } }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "dependencies": { + "is-property": "^1.0.2" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2467,7 +2486,6 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -2636,6 +2654,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -2803,6 +2827,12 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -3102,6 +3132,53 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/mysql2": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", + "dev": true, + "dependencies": { + "denque": "^2.0.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "lru-cache": "^6.0.0", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "dev": true, + "dependencies": { + "lru-cache": "^4.1.3" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/named-placeholders/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/named-placeholders/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, "node_modules/nan": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", @@ -3540,6 +3617,12 @@ "node": ">=10" } }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -3991,6 +4074,12 @@ "node": ">=10" } }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=", + "dev": true + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -4165,6 +4254,15 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -5747,6 +5845,12 @@ "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true }, + "denque": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", + "integrity": "sha512-tfiWc6BQLXNLpNiR5iGd0Ocu3P3VpxfzFiqubLgMfhfOw9WyvgJBd46CClNn9k3qfbjvT//0cf7AlYRX/OslMQ==", + "dev": true + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -6386,6 +6490,15 @@ "globule": "^1.0.0" } }, + "generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "requires": { + "is-property": "^1.0.2" + } + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -6611,7 +6724,6 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -6741,6 +6853,12 @@ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", "dev": true }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -6890,6 +7008,12 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", + "dev": true + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -7117,6 +7241,49 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "mysql2": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz", + "integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==", + "dev": true, + "requires": { + "denque": "^2.0.1", + "generate-function": "^2.3.1", + "iconv-lite": "^0.6.3", + "long": "^4.0.0", + "lru-cache": "^6.0.0", + "named-placeholders": "^1.1.2", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + } + }, + "named-placeholders": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.2.tgz", + "integrity": "sha512-wiFWqxoLL3PGVReSZpjLVxyJ1bRqe+KKJVbr4hGs1KWfTZTQyezHFBbuKj9hsizHyGV2ne7EMjHdxEGAybD5SA==", + "dev": true, + "requires": { + "lru-cache": "^4.1.3" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, "nan": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", @@ -7444,6 +7611,12 @@ "retry": "^0.12.0" } }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -7755,6 +7928,12 @@ "lru-cache": "^6.0.0" } }, + "seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha1-1WgS4cAXpuTnw+Ojeh2m143TyT4=", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -7894,6 +8073,12 @@ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, + "sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "dev": true + }, "sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", diff --git a/package.json b/package.json index dfae2c8..10370df 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "eslint": "^7.32.0", "eslint-plugin-svelte3": "^3.2.1", "fa-svelte": "^3.1.0", + "mysql2": "^2.3.3", "node-sass": "^7.0.1", "sass": "^1.49.9", "svelte": "^3.44.0", diff --git a/scripts/goose-mysql/20220313115117_scope.sql b/scripts/goose-mysql/20220313115117_scope.sql new file mode 100644 index 0000000..d61fb9c --- /dev/null +++ b/scripts/goose-mysql/20220313115117_scope.sql @@ -0,0 +1,13 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE scope ( + `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, + `name` VARCHAR(255) NOT NULL, + `abbreviation` CHAR(8) NOT NULL +) +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS scope; +-- +goose StatementEnd diff --git a/scripts/goose-mysql/20220313115122_scope_member.sql b/scripts/goose-mysql/20220313115122_scope_member.sql new file mode 100644 index 0000000..a810f04 --- /dev/null +++ b/scripts/goose-mysql/20220313115122_scope_member.sql @@ -0,0 +1,17 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE scope_member ( + `scope_id` INT NOT NULL, + `user_id` CHAR(36) NOT NULL, + `name` VARCHAR(63) NOT NULL, + `owner` BOOLEAN NOT NULL, + + PRIMARY KEY (`scope_id`, `user_id`), + UNIQUE (`scope_id`, `name`) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS scope_member; +-- +goose StatementEnd diff --git a/src/app.d.ts b/src/app.d.ts index 2adaf08..20332a2 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,11 +1,18 @@ /// +import type { ScopeEntry } from "$lib/models/scope"; import type Scope from "$lib/models/scope"; +import type User from "$lib/models/user"; // See https://kit.svelte.dev/docs/types#the-app-namespace // for information about these interfaces declare global { declare namespace App { + interface Locals { + user: User, + scopes: ScopeEntry[], + } + // interface Platform {} // interface Session {} interface Stuff { diff --git a/src/app.html b/src/app.html index fb8734a..d6b2618 100644 --- a/src/app.html +++ b/src/app.html @@ -7,7 +7,7 @@ \ No newline at end of file diff --git a/src/lib/components/layout/Entry.svelte b/src/lib/components/layout/Entry.svelte index 3b25eef..74f0272 100644 --- a/src/lib/components/layout/Entry.svelte +++ b/src/lib/components/layout/Entry.svelte @@ -9,15 +9,16 @@ display: block; text-decoration: none; color: $color-entry9; - background-color: $color-entry1; + background-color: $color-entry1-transparent; border-bottom-right-radius: 0.75em; margin: 0.5em 0; padding: 0.25em 0.5ch; transition: 250ms; overflow-y: hidden; + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.3); } div.entry:hover { - background-color: $color-entry2; + background-color: $color-entry2-transparent; color: $color-entry11; } diff --git a/src/lib/components/layout/EntryStatusLine.svelte b/src/lib/components/layout/EntryStatusLine.svelte index 749eb63..26512f6 100644 --- a/src/lib/components/layout/EntryStatusLine.svelte +++ b/src/lib/components/layout/EntryStatusLine.svelte @@ -1,49 +1,16 @@
- - {statusText} + +
diff --git a/src/lib/components/layout/Header.svelte b/src/lib/components/layout/Header.svelte new file mode 100644 index 0000000..dc11b3b --- /dev/null +++ b/src/lib/components/layout/Header.svelte @@ -0,0 +1,39 @@ + + +
+

+

{subtitle}

+
+ + \ No newline at end of file diff --git a/src/lib/components/layout/Icon.svelte b/src/lib/components/layout/Icon.svelte index 9e0041b..8b36130 100644 --- a/src/lib/components/layout/Icon.svelte +++ b/src/lib/components/layout/Icon.svelte @@ -38,6 +38,8 @@ import { faThumbtack } from "@fortawesome/free-solid-svg-icons/faThumbtack"; import { faHistory } from "@fortawesome/free-solid-svg-icons/faHistory"; import { faLightbulb } from "@fortawesome/free-solid-svg-icons/faLightbulb"; + import { faChevronRight } from "@fortawesome/free-solid-svg-icons/faChevronRight"; + import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown"; const icons = { "clock": faClock, @@ -58,6 +60,8 @@ "calendar": faCalendar, "expand": faExpand, "search": faSearch, + "chevron_right": faChevronRight, + "chevron_down": faChevronDown, }; export type IconName = keyof typeof icons; diff --git a/src/lib/components/layout/MenuCategory.svelte b/src/lib/components/layout/MenuCategory.svelte new file mode 100644 index 0000000..21692b3 --- /dev/null +++ b/src/lib/components/layout/MenuCategory.svelte @@ -0,0 +1,67 @@ + + +
+
+ {#if expanded !== null} + + {/if} + {#if status !== null} + + + + + {:else} + {title} + {/if} +
+ {#if expanded !== false} +
+ +
+ {/if} +
+ + \ No newline at end of file diff --git a/src/lib/components/layout/MenuItem.svelte b/src/lib/components/layout/MenuItem.svelte new file mode 100644 index 0000000..19d4466 --- /dev/null +++ b/src/lib/components/layout/MenuItem.svelte @@ -0,0 +1,77 @@ + + +{#if !!href} + + + +{:else} + +{/if} + + \ No newline at end of file diff --git a/src/lib/components/layout/StatusIcon.svelte b/src/lib/components/layout/StatusIcon.svelte new file mode 100644 index 0000000..7057303 --- /dev/null +++ b/src/lib/components/layout/StatusIcon.svelte @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/src/lib/components/layout/StatusText.svelte b/src/lib/components/layout/StatusText.svelte new file mode 100644 index 0000000..acdbe5a --- /dev/null +++ b/src/lib/components/layout/StatusText.svelte @@ -0,0 +1,19 @@ + + +{STATUS_NAMES[status]} + + \ No newline at end of file diff --git a/src/lib/config.ts b/src/lib/config.ts new file mode 100644 index 0000000..2adb1c4 --- /dev/null +++ b/src/lib/config.ts @@ -0,0 +1,18 @@ +import type { Database } from "./database/interfaces"; +import MysqlDB from "./database/mysql/database"; + +let databasePromise: Promise | null = null; +let databaseTime: number = 0; + +const config = { + database() { + if (databasePromise == null || Date.now() < (databaseTime - 60000)) { + databasePromise = MysqlDB.connectEnv(); + databaseTime = Date.now(); + } + + return databasePromise; + } +}; + +export default config; \ No newline at end of file diff --git a/src/lib/database/interfaces.ts b/src/lib/database/interfaces.ts new file mode 100644 index 0000000..699f8b5 --- /dev/null +++ b/src/lib/database/interfaces.ts @@ -0,0 +1,31 @@ +import type { ScopeEntry, ScopeInput } from "$lib/models/scope"; +import type Scope from "$lib/models/scope"; +import type { StatEntry } from "$lib/models/stat"; +import type Stat from "$lib/models/stat"; + +export interface Database { + userId: string + + scopes(): ScopeRepo + stats(scopeId: number): StatRepo + withUser(userId: string): Database +} + +export interface ScopeRepo { + userId: string + + find(id: number): Promise + list(): Promise + create(input: ScopeInput): Promise + update(id: number, input: Partial): Promise + delete(id: number): Promise +} + +export interface StatRepo { + userId: string + scopeId: number + + find(id: number): Promise + findEntries(...ids: number[]): Promise + list(): Promise +} \ No newline at end of file diff --git a/src/lib/database/mysql/database.ts b/src/lib/database/mysql/database.ts new file mode 100644 index 0000000..a812582 --- /dev/null +++ b/src/lib/database/mysql/database.ts @@ -0,0 +1,48 @@ +import {createPool} from "mysql2/promise" +import type {Pool} from "mysql2/promise"; + +import type { Database, ScopeRepo, StatRepo } from "../interfaces"; +import MysqlDBScopes from "./scopes"; + + +export default class MysqlDB implements Database { + connection: Pool + userId: string; + + private constructor(userId: string, connection: Connection) { + this.userId = userId; + this.connection = connection; + } + + scopes(): ScopeRepo { + return new MysqlDBScopes(this.connection, this.userId); + } + stats(scopeId: number): StatRepo { + throw new Error("Method not implemented."); + } + + withUser(userId: string): Database { + return new MysqlDB(userId, this.connection); + } + + static async connectEnv(): Promise { + return this.connect( + process.env.STUFFLOG3_MYSQL_HOST, + parseInt(process.env.STUFFLOG3_MYSQL_PORT), + process.env.STUFFLOG3_MYSQL_USERNAME, + process.env.STUFFLOG3_MYSQL_PASSWORD, + process.env.STUFFLOG3_MYSQL_SCHEMA, + ) + } + + static async connect(host: string, port: number, user: string, password: string, database: string): Promise { + const connection = await createPool({ + host, user, database, password, port, + waitForConnections: true, + connectionLimit: 20, + queueLimit: 0, + }); + + return new MysqlDB("", connection); + } +} \ No newline at end of file diff --git a/src/lib/database/mysql/scopes.ts b/src/lib/database/mysql/scopes.ts new file mode 100644 index 0000000..58c0928 --- /dev/null +++ b/src/lib/database/mysql/scopes.ts @@ -0,0 +1,43 @@ +import type {Pool} from "mysql2/promise"; + +import type scope from "$lib/models/scope"; +import type { ScopeEntry, ScopeInput } from "$lib/models/scope"; +import type { ScopeRepo } from "../interfaces"; + +export default class MysqlDBScopes implements ScopeRepo { + userId: string; + connection: Pool; + + constructor(connection: Pool, userId: string) { + this.connection = connection; + this.userId = userId; + } + + find(id: number): Promise { + throw new Error("Method not implemented."); + } + async list(): Promise { + const [rows] = await this.connection.execute(` + SELECT scope.*, scope_member.name as display_name + FROM scope + INNER JOIN scope_member ON id = scope_id + WHERE user_id = ? + `, [this.userId]); + + return (rows as any[]).map(r => ({ + id: r.id, + name: r.name, + abbreviation: r.abbreviation, + displayName: r.display_name, + })) + } + create(input: ScopeInput): Promise { + throw new Error("Method not implemented."); + } + update(id: number, input: Partial): Promise { + throw new Error("Method not implemented."); + } + delete(id: number): Promise { + throw new Error("Method not implemented."); + } +} \ No newline at end of file diff --git a/src/lib/models/scope.ts b/src/lib/models/scope.ts index 2cf073c..ccaeeed 100644 --- a/src/lib/models/scope.ts +++ b/src/lib/models/scope.ts @@ -10,4 +10,16 @@ export interface ScopeEntry { id: number name: string abbreviation: string + displayName: string +} + +export interface ScopeInput { + name: string + abbreviation: string + displayName: string +} + +export interface ScopeMember { + name: string + owner: boolean } \ No newline at end of file diff --git a/src/lib/models/status.ts b/src/lib/models/status.ts index a9e2630..4fbe089 100644 --- a/src/lib/models/status.ts +++ b/src/lib/models/status.ts @@ -8,6 +8,22 @@ enum Status { Dropped = 6, } + +export interface LabeledStatus { + value: Status + name: string +} + +export const allStatuses: LabeledStatus[] = [ + {value: Status.Active, name: "Active"}, + {value: Status.Background, name: "Background"}, + {value: Status.Available, name: "Available"}, + {value: Status.Blocked, name: "Blocked"}, + {value: Status.Completed, name: "Completed"}, + {value: Status.Failed, name: "Failed"}, + {value: Status.Dropped, name: "Dropped"}, +]; + // Blocked -> Available -> Active -> Completed // -> Failed // -> Dropped diff --git a/src/lib/models/user.ts b/src/lib/models/user.ts new file mode 100644 index 0000000..9e43bcb --- /dev/null +++ b/src/lib/models/user.ts @@ -0,0 +1,4 @@ +export default interface User { + id: string + name: string +} \ No newline at end of file diff --git a/src/routes/[scope].json.ts b/src/routes/[scope].json.ts index ba0e393..c624bcc 100644 --- a/src/routes/[scope].json.ts +++ b/src/routes/[scope].json.ts @@ -30,7 +30,7 @@ export const get: RequestHandler = async({params}) => { const scopes: Scope[] = [ { - id: 1, name: "3D Modeling", abbreviation: "3D", + id: 1, name: "3D Modeling", abbreviation: "3D", displayName: "Test", stats: [ {id: 101, name: "Asset", weight: 0.1, description: "Some description"}, {id: 102, name: "Tweak", weight: 0.2, description: "Some description"}, @@ -55,7 +55,7 @@ const scopes: Scope[] = [ ] }, { - id: 2, name: "Roleplay", abbreviation: "RP", + id: 2, name: "Roleplay", abbreviation: "RP", displayName: "Test", stats: [ {id: 201, name: "Story", weight: 3, description: "Some description"}, {id: 202, name: "Story Word", weight: 0.002, description: "Some description"}, @@ -67,9 +67,14 @@ const scopes: Scope[] = [ {id: 208, name: "Wiki Complexity", weight: 0.5, description: "Some description"}, ], activeProjects: [ - {id: 2001, name: "Ilyna T'Rea", status: Status.Active}, - {id: 2002, name: "Renala T'Iavay", status: Status.Active}, - {id: 2003, name: "Background Stories", status: Status.Background}, + {id: 2001, name: "Ilyna: The Morning After", status: Status.Active}, + {id: 2002, name: "Renala: Finding Her Voice", status: Status.Active}, + {id: 2003, name: "General Stories", status: Status.Background}, + {id: 2004, name: "Uvena: Finish Wiki Page", status: Status.Active}, + {id: 2005, name: "Va'ynna: Cracking at the Seams", status: Status.Active}, + {id: 2006, name: "Witch Hunt: Sala's Backlog", status: Status.Active}, + {id: 2007, name: "Ilyna: Complete Wiki Page", status: Status.Completed}, + {id: 2008, name: "Renala: Complete Wiki Page", status: Status.Active}, ] }, //{id: 3, name: "Minecraft", abbreviation: "MC"}, diff --git a/src/routes/[scope]/__layout.svelte b/src/routes/[scope]/__layout.svelte index d707e8f..85a1058 100644 --- a/src/routes/[scope]/__layout.svelte +++ b/src/routes/[scope]/__layout.svelte @@ -19,14 +19,92 @@ return; } + + const STATUS_ORDER = [ + Status.Active, + Status.Background, + Status.Available, + Status.Blocked, + Status.Completed, + Status.Failed, + Status.Dropped, + ] -

{scope.name}

+
+
+ +
+
+
{scope.abbreviation}
+ + Overview + History + + {#each STATUS_ORDER as status (status)} + {#if projectsByStatus[status].length > 0} + + {#each projectsByStatus[status] as project (project.id)} + { + project.name.includes(": ") ? project.name.split(": ").slice(1).join(": ") : project.name + } + {/each} + + {/if} + {/each} +
+
+ + \ No newline at end of file diff --git a/src/routes/[scope]/index.svelte b/src/routes/[scope]/index.svelte index 8848950..0c58f65 100644 --- a/src/routes/[scope]/index.svelte +++ b/src/routes/[scope]/index.svelte @@ -1,5 +1,5 @@ -

{$page.stuff.scope.name}

\ No newline at end of file +

{$page.stuff.scope.name}

+ + \ No newline at end of file diff --git a/src/routes/__layout.svelte b/src/routes/__layout.svelte new file mode 100644 index 0000000..6b267ee --- /dev/null +++ b/src/routes/__layout.svelte @@ -0,0 +1,10 @@ + + + \ No newline at end of file diff --git a/src/routes/index.svelte b/src/routes/index.svelte index 81bee7c..dd44084 100644 --- a/src/routes/index.svelte +++ b/src/routes/index.svelte @@ -17,7 +17,6 @@ error: `Failed: ${res.status}` }; } - -
-

Stufflog

-

Logging your stuff.

-
+
Stufflog
@@ -76,28 +72,6 @@