summaryrefslogtreecommitdiffstats
path: root/apps/accounts
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-06-01 22:10:32 +0200
committerivarlovlie <git@ivarlovlie.no>2022-06-01 22:10:32 +0200
commita640703f2da8815dc26ad1600a6f206be1624379 (patch)
treedbda195fb5783d16487e557e06471cf848b75427 /apps/accounts
downloadgreatoffice-a640703f2da8815dc26ad1600a6f206be1624379.tar.xz
greatoffice-a640703f2da8815dc26ad1600a6f206be1624379.zip
feat: Initial after clean slate
Diffstat (limited to 'apps/accounts')
-rw-r--r--apps/accounts/.version1
-rw-r--r--apps/accounts/.version-dev1
-rw-r--r--apps/accounts/CHANGELOG.md53
-rwxr-xr-xapps/accounts/build_and_push.sh76
-rw-r--r--apps/accounts/cliff.toml62
-rw-r--r--apps/accounts/src/_assets/pre.css128
-rw-r--r--apps/accounts/src/_assets/pwa/android-chrome-192x192.pngbin0 -> 3291 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/android-chrome-512x512.pngbin0 -> 9687 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/apple-touch-icon.pngbin0 -> 2769 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/browserconfig.xml9
-rw-r--r--apps/accounts/src/_assets/pwa/favicon-16x16.pngbin0 -> 636 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/favicon-32x32.pngbin0 -> 907 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/favicon.icobin0 -> 15086 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/favicon.svg4
-rw-r--r--apps/accounts/src/_assets/pwa/manifest.json28
-rw-r--r--apps/accounts/src/_assets/pwa/mstile-144x144.pngbin0 -> 3109 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/mstile-150x150.pngbin0 -> 3238 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/mstile-310x150.pngbin0 -> 3501 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/mstile-310x310.pngbin0 -> 6823 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/mstile-70x70.pngbin0 -> 2238 bytes
-rw-r--r--apps/accounts/src/_assets/pwa/safari-pinned-tab.svg50
-rw-r--r--apps/accounts/src/app/index.d.ts48
-rw-r--r--apps/accounts/src/app/index.scss21
-rw-r--r--apps/accounts/src/app/index.svelte61
-rw-r--r--apps/accounts/src/app/index.ts14
-rw-r--r--apps/accounts/src/app/pages/_layout.svelte142
-rw-r--r--apps/accounts/src/app/pages/forgot.svelte99
-rw-r--r--apps/accounts/src/app/pages/login.svelte145
-rw-r--r--apps/accounts/src/app/pages/not-found.svelte23
-rw-r--r--apps/accounts/src/app/pages/reset-password.svelte135
-rw-r--r--apps/accounts/src/app/pages/sign-up.svelte128
-rw-r--r--apps/accounts/src/index.html63
-rw-r--r--apps/accounts/src/package.json22
-rw-r--r--apps/accounts/src/pnpm-lock.yaml769
-rw-r--r--apps/accounts/src/tsconfig.json30
-rw-r--r--apps/accounts/src/vite.config.ts30
36 files changed, 2142 insertions, 0 deletions
diff --git a/apps/accounts/.version b/apps/accounts/.version
new file mode 100644
index 0000000..722aa6d
--- /dev/null
+++ b/apps/accounts/.version
@@ -0,0 +1 @@
+v1-accounts
diff --git a/apps/accounts/.version-dev b/apps/accounts/.version-dev
new file mode 100644
index 0000000..6eeb9c2
--- /dev/null
+++ b/apps/accounts/.version-dev
@@ -0,0 +1 @@
+v12-accounts-dev
diff --git a/apps/accounts/CHANGELOG.md b/apps/accounts/CHANGELOG.md
new file mode 100644
index 0000000..4d4ed65
--- /dev/null
+++ b/apps/accounts/CHANGELOG.md
@@ -0,0 +1,53 @@
+# Changelog
+
+## [unreleased]
+
+### Miscellaneous Tasks
+
+- Bump version
+- Update CHANGELOG.md for v4-projects-dev
+- Bump version
+- Update CHANGELOG.md for v3-projects-dev
+- Bump version
+- Bump version
+- Update CHANGELOG.md for v12-accounts-dev
+
+### Refactor
+
+- Name path basers based on the app it bases
+
+## [unreleased]
+
+### Miscellaneous Tasks
+
+- Bump version
+- Update CHANGELOG.md for v11-accounts-dev
+
+## [unreleased]
+
+### Miscellaneous Tasks
+
+- Bump version
+- Update CHANGELOG.md for v14-web-app-dev
+- Bump version
+- Update CHANGELOG.md for v9-web-app-dev
+
+## [unreleased]
+
+### Bug Fixes
+
+- .
+
+### Miscellaneous Tasks
+
+- Bump version
+- Update CHANGELOG.md for v8-web-app-dev
+
+## [unreleased]
+
+### Miscellaneous Tasks
+
+- Bump version
+- Bump version
+- Update CHANGELOG.md for v7-web-app-dev
+
diff --git a/apps/accounts/build_and_push.sh b/apps/accounts/build_and_push.sh
new file mode 100755
index 0000000..bd349ff
--- /dev/null
+++ b/apps/accounts/build_and_push.sh
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+
+set -Eueo pipefail
+
+APP_NAME="accounts";
+CURRENT_DEV_VERSION=$(cat .version-dev)
+CURRENT_DEV_VERSION_INT=${CURRENT_DEV_VERSION//[!0-9]/}
+CURRENT_VERSION=$(cat .version)
+CURRENT_VERSION_INT=${CURRENT_VERSION//[!0-9]/}
+if [ ${1-prod} == "dev" ]; then
+ NEW_VERSION="v$((CURRENT_DEV_VERSION_INT+1))-$APP_NAME-dev"
+ OLD_VERSION=$CURRENT_DEV_VERSION
+else
+ NEW_VERSION="v$((CURRENT_VERSION_INT+1))-$APP_NAME"
+ OLD_VERSION=$CURRENT_VERSION
+fi
+# Check for uncommited changes and optionally commit them
+if [ "$(git status --untracked-files=no --porcelain)" ]; then
+ echo "Unclean git tree! press CTRL+C to exit or press ENTER to commit and push to the default branch"
+ read -n 1
+
+ read -p "Enter commit message: " COMMIT_MESSAGE
+ git add ../../
+ git commit --quiet -m "$COMMIT_MESSAGE"
+fi
+
+if [ ${1-prod} == "dev" ]; then
+ echo $NEW_VERSION >| .version-dev
+ git add .version-dev
+else
+ echo $NEW_VERSION >| .version
+ git add .version
+fi
+
+echo "Starting build of $APP_NAME@$NEW_VERSION at $(date -u)..."
+echo
+
+git commit --quiet -m "chore(release): Bump version";
+
+read -p "Do you want to tag this build? (y/n) " -n 1 -r
+echo
+if [[ $REPLY =~ ^[Yy]$ ]]
+then
+ read -p "Enter tag message (can be empty): " TAG_MESSAGE
+ commit_msg="chore(release): Update CHANGELOG.md for $NEW_VERSION"
+ git cliff -r ../../ $OLD_VERSION..HEAD --with-commit "$commit_msg" --prepend CHANGELOG.md
+ git add CHANGELOG.md
+ git commit --quiet -m "$commit_msg";
+ git tag -am "$TAG_MESSAGE" $NEW_VERSION
+fi
+
+read -p "Do you want to push the latest commits and tags to origin? (y/n) " -n 1 -r
+echo
+if [[ $REPLY =~ ^[Yy]$ ]]
+then
+ echo "Pushing latest changes to remotes..."
+ echo
+ git push --quiet --follow-tags
+fi
+
+pushd src
+pnpm run build
+
+cd build
+echo "$NEW_VERSION" >version.txt
+
+
+if [ ${1-prod} == "dev" ]; then
+ scp -r * contabo-fast-1:services/public/a.dev.greatoffice.life/www
+else
+ echo "Pushing to production in 10 sec, press CTRL+C to cancel"
+ sleep 10
+ scp -r * contabo-fast-1:services/public/a.greatoffice.life/www
+fi
+
+popd
diff --git a/apps/accounts/cliff.toml b/apps/accounts/cliff.toml
new file mode 100644
index 0000000..955a72b
--- /dev/null
+++ b/apps/accounts/cliff.toml
@@ -0,0 +1,62 @@
+# configuration file for git-cliff (0.1.0)
+
+[changelog]
+# changelog header
+header = """
+# Changelog\n
+"""
+# template for the changelog body
+# https://tera.netlify.app/docs/#introduction
+body = """
+{% if version %}\
+ ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
+{% else %}\
+ ## [unreleased]
+{% endif %}\
+{% for group, commits in commits | group_by(attribute="group") %}
+ ### {{ group | upper_first }}
+ {% for commit in commits %}
+ - {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\
+ {% endfor %}
+{% endfor %}\n
+"""
+# remove the leading and trailing whitespace from the template
+trim = true
+# changelog footer
+footer = """
+<!-- generated by git-cliff -->
+"""
+
+[git]
+# parse the commits based on https://www.conventionalcommits.org
+conventional_commits = true
+# filter out the commits that are not conventional
+filter_unconventional = true
+# regex for preprocessing the commit messages
+commit_preprocessors = [
+ { pattern = "([ \\n])(([a-f0-9]{7})[a-f0-9]*)", replace = "${1}commit # [${3}](https://git.ivarlovlie.no/time-tracker/commit/${2})" },
+ { pattern = "https://git.ivarlovlie.no/time-tracker/commit/([a-f0-9]{7})[a-f0-9]*", replace = "commit # [${1}](${0})" },
+]
+# regex for parsing and grouping commits
+commit_parsers = [
+ { message = "^feat", group = "Features" },
+ { message = "^fix", group = "Bug Fixes" },
+ { message = "^doc", group = "Documentation" },
+ { message = "^perf", group = "Performance" },
+ { message = "^refactor", group = "Refactor" },
+ { message = "^style", group = "Styling" },
+ { message = "^test", group = "Testing" },
+ { message = "^chore", group = "Miscellaneous Tasks" },
+]
+# filter out the commits that are not matched by commit parsers
+filter_commits = true
+# glob pattern for matching git tags
+tag_pattern = "v.*"
+# regex for skipping tags
+skip_tags = "v0.1.0-beta.1"
+# regex for ignoring tags
+ignore_tags = ""
+# sort the tags chronologically
+date_order = true
+# sort the commits inside sections by oldest/newest order
+sort_commits = "newest"
diff --git a/apps/accounts/src/_assets/pre.css b/apps/accounts/src/_assets/pre.css
new file mode 100644
index 0000000..9c9446e
--- /dev/null
+++ b/apps/accounts/src/_assets/pre.css
@@ -0,0 +1,128 @@
+:root {
+ --loader-primary: hsl(250, 84%, 54%);
+ --loader-accent: hsl(342, 89%, 48%);
+ --loader-contrast: hsl(180, 1%, 84%);
+ --loader-easing: cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+
+[data-theme="dark"] :root {
+ --loader-primary: hsl(250, 93%, 65%);
+ --loader-accent: hsl(342, 92%, 54%);
+ --loader-contrast: hsl(208, 12%, 24%);
+ --loader-easing: cubic-bezier(0.645, 0.045, 0.355, 1);
+}
+
+[data-theme="dark"] {
+ background-color: hsl(203, 24%, 13%);
+}
+
+.fill-loader {
+ position: relative;
+ overflow: hidden;
+ display: inline-block;
+ margin: 3rem;
+}
+
+.fill-loader__fill {
+ position: absolute;
+}
+
+@supports (-webkit-animation-name: this) or (animation-name: this) {
+ .fill-loader__label {
+ position: absolute;
+ clip: rect(1px, 1px, 1px, 1px);
+ -webkit-clip-path: inset(50%);
+ clip-path: inset(50%);
+ }
+}
+
+@supports (-webkit-animation-name: this) or (animation-name: this) {
+ .fill-loader--v4 {
+ width: 90%;
+ max-width: 300px;
+ }
+
+ .fill-loader--v4 .fill-loader__base {
+ height: 4px;
+ background-color: var(--loader-contrast);
+ }
+
+ .fill-loader--v4 .fill-loader__fill {
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 100%;
+ background-color: var(--loader-primary);
+ -webkit-animation: fill-loader-4 1.6s infinite var(--loader-easing);
+ animation: fill-loader-4 1.6s infinite var(--loader-easing);
+ will-change: left, right;
+ }
+}
+
+@-webkit-keyframes fill-loader-4 {
+ 0% {
+ left: 0;
+ right: 100%;
+ background-color: var(--loader-primary);
+ }
+
+ 10%,
+ 60% {
+ left: 0;
+ }
+
+ 40%,
+ 90% {
+ right: 0;
+ }
+
+ 50% {
+ left: 100%;
+ background-color: var(--loader-primary);
+ }
+
+ 51% {
+ left: 0;
+ right: 100%;
+ background-color: var(--loader-accent);
+ }
+
+ 100% {
+ left: 100%;
+ background-color: var(--loader-accent);
+ }
+}
+
+@keyframes fill-loader-4 {
+ 0% {
+ left: 0;
+ right: 100%;
+ background-color: var(--loader-primary);
+ }
+
+ 10%,
+ 60% {
+ left: 0;
+ }
+
+ 40%,
+ 90% {
+ right: 0;
+ }
+
+ 50% {
+ left: 100%;
+ background-color: var(--loader-primary);
+ }
+
+ 51% {
+ left: 0;
+ right: 100%;
+ background-color: var(--loader-accent);
+ }
+
+ 100% {
+ left: 100%;
+ background-color: var(--loader-accent);
+ }
+}
diff --git a/apps/accounts/src/_assets/pwa/android-chrome-192x192.png b/apps/accounts/src/_assets/pwa/android-chrome-192x192.png
new file mode 100644
index 0000000..5c098bc
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/android-chrome-192x192.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/android-chrome-512x512.png b/apps/accounts/src/_assets/pwa/android-chrome-512x512.png
new file mode 100644
index 0000000..973a1c3
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/android-chrome-512x512.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/apple-touch-icon.png b/apps/accounts/src/_assets/pwa/apple-touch-icon.png
new file mode 100644
index 0000000..b4d9773
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/apple-touch-icon.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/browserconfig.xml b/apps/accounts/src/_assets/pwa/browserconfig.xml
new file mode 100644
index 0000000..b3930d0
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/browserconfig.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<browserconfig>
+ <msapplication>
+ <tile>
+ <square150x150logo src="/mstile-150x150.png"/>
+ <TileColor>#da532c</TileColor>
+ </tile>
+ </msapplication>
+</browserconfig>
diff --git a/apps/accounts/src/_assets/pwa/favicon-16x16.png b/apps/accounts/src/_assets/pwa/favicon-16x16.png
new file mode 100644
index 0000000..5dde9f9
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/favicon-16x16.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/favicon-32x32.png b/apps/accounts/src/_assets/pwa/favicon-32x32.png
new file mode 100644
index 0000000..9cef4c4
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/favicon-32x32.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/favicon.ico b/apps/accounts/src/_assets/pwa/favicon.ico
new file mode 100644
index 0000000..89c7542
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/favicon.ico
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/favicon.svg b/apps/accounts/src/_assets/pwa/favicon.svg
new file mode 100644
index 0000000..964dbb8
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/favicon.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-stopwatch" viewBox="0 0 16 16">
+ <path d="M8.5 5.6a.5.5 0 1 0-1 0v2.9h-3a.5.5 0 0 0 0 1H8a.5.5 0 0 0 .5-.5V5.6z"/>
+ <path d="M6.5 1A.5.5 0 0 1 7 .5h2a.5.5 0 0 1 0 1v.57c1.36.196 2.594.78 3.584 1.64a.715.715 0 0 1 .012-.013l.354-.354-.354-.353a.5.5 0 0 1 .707-.708l1.414 1.415a.5.5 0 1 1-.707.707l-.353-.354-.354.354a.512.512 0 0 1-.013.012A7 7 0 1 1 7 2.071V1.5a.5.5 0 0 1-.5-.5zM8 3a6 6 0 1 0 .001 12A6 6 0 0 0 8 3z"/>
+</svg> \ No newline at end of file
diff --git a/apps/accounts/src/_assets/pwa/manifest.json b/apps/accounts/src/_assets/pwa/manifest.json
new file mode 100644
index 0000000..4c550fe
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/manifest.json
@@ -0,0 +1,28 @@
+{
+ "manifest_version": 2,
+ "version": "0.1",
+ "name": "Time Tracker",
+ "short_name": "Time Tracker",
+ "display": "standalone",
+ "background_color": "#fff",
+ "theme_color": "#4D3DF7",
+ "start_url": ".",
+ "orientation": "portrait",
+ "icons": [
+ {
+ "src": "/favicon.svg",
+ "purpose": "maskable any",
+ "sizes": "any"
+ },
+ {
+ "src": "/pwa/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/pwa/android-chrome-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+}
diff --git a/apps/accounts/src/_assets/pwa/mstile-144x144.png b/apps/accounts/src/_assets/pwa/mstile-144x144.png
new file mode 100644
index 0000000..84d94cb
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/mstile-144x144.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/mstile-150x150.png b/apps/accounts/src/_assets/pwa/mstile-150x150.png
new file mode 100644
index 0000000..b1398ae
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/mstile-150x150.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/mstile-310x150.png b/apps/accounts/src/_assets/pwa/mstile-310x150.png
new file mode 100644
index 0000000..76b16a0
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/mstile-310x150.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/mstile-310x310.png b/apps/accounts/src/_assets/pwa/mstile-310x310.png
new file mode 100644
index 0000000..d8e4097
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/mstile-310x310.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/mstile-70x70.png b/apps/accounts/src/_assets/pwa/mstile-70x70.png
new file mode 100644
index 0000000..0df1e8c
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/mstile-70x70.png
Binary files differ
diff --git a/apps/accounts/src/_assets/pwa/safari-pinned-tab.svg b/apps/accounts/src/_assets/pwa/safari-pinned-tab.svg
new file mode 100644
index 0000000..ba2220c
--- /dev/null
+++ b/apps/accounts/src/_assets/pwa/safari-pinned-tab.svg
@@ -0,0 +1,50 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
+ width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
+ preserveAspectRatio="xMidYMid meet">
+<metadata>
+Created by potrace 1.14, written by Peter Selinger 2001-2017
+</metadata>
+<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
+fill="#000000" stroke="none">
+<path d="M3195 6780 c-116 -3 -211 -10 -226 -17 -39 -17 -105 -98 -116 -142
+-19 -72 -2 -146 45 -202 26 -31 96 -69 131 -72 25 -2 31 -6 32 -27 1 -27 1
+-198 0 -216 -1 -6 -47 -19 -103 -28 -160 -28 -451 -107 -533 -146 -11 -5 -51
+-21 -90 -36 -60 -23 -246 -112 -325 -155 -431 -236 -834 -619 -1101 -1045
+-207 -328 -364 -733 -423 -1089 -51 -307 -61 -583 -31 -875 26 -261 119 -615
+225 -861 185 -430 432 -773 800 -1108 75 -69 387 -301 405 -301 1 0 33 -18 70
+-40 209 -128 602 -288 796 -325 12 -2 29 -7 39 -10 72 -23 273 -56 435 -73
+144 -14 601 -5 658 13 7 2 37 7 67 10 273 33 616 141 904 283 725 357 1275
+982 1542 1754 55 159 113 395 129 523 4 28 8 57 10 65 2 8 7 47 10 85 3 39 8
+93 10 120 6 66 6 327 0 390 -2 28 -7 82 -10 120 -3 39 -11 99 -16 135 -6 36
+-13 79 -16 95 -15 98 -61 279 -103 405 -121 372 -298 694 -542 993 -27 32 -48
+61 -48 65 0 4 35 41 78 84 l77 76 90 -90 c53 -54 108 -99 134 -110 62 -28 130
+-25 191 8 95 52 135 151 103 257 -13 46 -44 79 -362 397 -322 323 -351 349
+-398 363 -148 44 -287 -61 -285 -215 1 -62 35 -118 126 -208 47 -47 86 -87 86
+-90 0 -6 -91 -101 -132 -138 l-25 -23 -46 38 c-264 223 -584 405 -924 528 -92
+34 -320 100 -376 109 -15 3 -35 7 -45 10 -9 3 -34 7 -54 10 -86 13 -113 18
+-117 22 -2 2 -4 56 -4 121 l0 118 29 9 c66 19 114 47 139 80 72 95 65 215 -18
+296 -58 56 -83 60 -402 63 -159 1 -380 0 -490 -3z m560 -1104 c224 -24 547
+-99 670 -156 11 -5 56 -24 100 -41 90 -37 282 -134 306 -155 8 -8 19 -14 23
+-14 13 0 192 -124 286 -199 97 -77 297 -270 364 -351 237 -288 405 -598 509
+-941 30 -98 44 -157 72 -299 3 -14 8 -47 11 -75 3 -27 7 -56 10 -63 22 -69 21
+-519 -1 -642 -1 -8 -6 -40 -10 -70 -4 -30 -9 -64 -11 -75 -2 -11 -7 -33 -10
+-50 -3 -16 -14 -66 -26 -110 -11 -44 -21 -84 -22 -90 -18 -79 -93 -275 -154
+-408 -39 -83 -158 -296 -171 -307 -3 -3 -26 -34 -50 -70 -116 -169 -312 -384
+-466 -508 -38 -32 -78 -65 -89 -74 -25 -22 -229 -160 -281 -189 -177 -99 -405
+-197 -570 -244 -126 -36 -305 -74 -375 -81 -19 -2 -48 -5 -65 -8 -121 -22
+-509 -22 -618 0 -12 2 -42 6 -67 10 -369 45 -795 215 -1125 448 -192 135 -399
+326 -517 476 -23 30 -48 61 -55 67 -57 60 -227 336 -291 473 -64 135 -150 365
+-167 444 -2 12 -6 30 -9 41 -28 120 -36 156 -41 193 -3 24 -7 53 -10 65 -32
+148 -38 552 -10 707 2 14 7 45 10 70 33 274 160 643 313 910 60 106 201 312
+232 340 3 3 23 28 45 55 22 28 85 96 140 151 347 352 768 590 1252 709 56 14
+118 27 137 30 20 2 61 9 93 14 32 6 92 13 133 17 41 3 76 7 77 8 6 5 368 -2
+428 -8z"/>
+<path d="M3423 4754 c-45 -17 -95 -61 -121 -109 -15 -27 -17 -93 -19 -650 -1
+-341 -2 -641 -3 -666 l0 -47 -679 0 -679 -1 -49 -24 c-59 -30 -76 -49 -104
+-112 -54 -122 23 -270 154 -293 23 -5 400 -8 837 -7 l795 1 42 22 c52 27 98
+82 112 136 8 29 10 273 9 826 -3 854 1 796 -59 867 -53 63 -153 87 -236 57z"/>
+</g>
+</svg>
diff --git a/apps/accounts/src/app/index.d.ts b/apps/accounts/src/app/index.d.ts
new file mode 100644
index 0000000..c044583
--- /dev/null
+++ b/apps/accounts/src/app/index.d.ts
@@ -0,0 +1,48 @@
+/* Use this file to declare any custom file extensions for importing */
+/* Use this folder to also add/extend a package d.ts file, if needed. */
+
+/* CSS MODULES */
+declare module "*.module.css" {
+ const classes: { [key: string]: string };
+ export default classes;
+}
+declare module "*.module.scss" {
+ const classes: { [key: string]: string };
+ export default classes;
+}
+
+/* CSS */
+declare module "*.css";
+declare module "*.scss";
+
+/* IMAGES */
+declare module "*.svg" {
+ const ref: string;
+ export default ref;
+}
+declare module "*.bmp" {
+ const ref: string;
+ export default ref;
+}
+declare module "*.gif" {
+ const ref: string;
+ export default ref;
+}
+declare module "*.jpg" {
+ const ref: string;
+ export default ref;
+}
+declare module "*.jpeg" {
+ const ref: string;
+ export default ref;
+}
+declare module "*.png" {
+ const ref: string;
+ export default ref;
+}
+
+/* CUSTOM: ADD YOUR OWN HERE */
+declare module "*.svelte" {
+ const value: any;
+ export default value;
+}
diff --git a/apps/accounts/src/app/index.scss b/apps/accounts/src/app/index.scss
new file mode 100644
index 0000000..56ac1c0
--- /dev/null
+++ b/apps/accounts/src/app/index.scss
@@ -0,0 +1,21 @@
+@use '../../web-shared/src/styles/base'as * with ($breakpoints: ('xs': "768px",
+ 'sm': "768px",
+ 'md': "1200px",
+ 'lg': "1200px",
+ 'xl': "1600px",
+ ),
+ $grid-columns: 12);
+
+@use '../../web-shared/src/styles/custom-style/colors';
+@use '../../web-shared/src/styles/custom-style/spacing';
+@use '../../web-shared/src/styles/custom-style/shared-styles';
+@use '../../web-shared/src/styles/custom-style/typography';
+@use '../../web-shared/src/styles/custom-style/icons';
+@use '../../web-shared/src/styles/custom-style/buttons';
+@use '../../web-shared/src/styles/custom-style/forms';
+@use '../../web-shared/src/styles/custom-style/util';
+
+@use '../../web-shared/src/styles/components/radios-checkboxes';
+@use '../../web-shared/src/styles/components/btn-states';
+@use '../../web-shared/src/styles/components/alert';
+@use '../../web-shared/src/styles/components/details';
diff --git a/apps/accounts/src/app/index.svelte b/apps/accounts/src/app/index.svelte
new file mode 100644
index 0000000..40fe6ae
--- /dev/null
+++ b/apps/accounts/src/app/index.svelte
@@ -0,0 +1,61 @@
+<svelte:options immutable={true}/>
+<svelte:window bind:online={online}/>
+
+<script>
+ import {projects_base} from "$shared/lib/configuration";
+ import Router from "svelte-spa-router";
+ import {wrap} from "svelte-spa-router/wrap";
+ import {is_active} from "$shared/lib/session";
+ import NotFound from "$app/pages/not-found.svelte";
+ import SignUp from "$app/pages/sign-up.svelte";
+ import Login from "$app/pages/login.svelte";
+ import Forgot from "$app/pages/forgot.svelte";
+ import Reset from "$app/pages/reset-password.svelte";
+ import PreHeader from "$shared/components/pre-header.svelte";
+
+ let online = true;
+
+ async function user_is_logged_in() {
+ if (await is_active()) {
+ location.replace(projects_base("#/home"));
+ }
+ return true;
+ }
+
+ const routes = {
+ "/login": wrap({
+ component: Login,
+ conditions: [user_is_logged_in],
+ }),
+ "/": wrap({
+ component: Login,
+ conditions: [user_is_logged_in],
+ }),
+ "/signup": wrap({
+ component: SignUp,
+ conditions: [user_is_logged_in],
+ }),
+ "/reset-password": wrap({
+ component: Reset,
+ conditions: [user_is_logged_in],
+ }),
+ "/forgot": wrap({
+ component: Forgot,
+ conditions: [user_is_logged_in],
+ }),
+ "*": NotFound,
+ };
+</script>
+
+<PreHeader show="{!online}">You seem to be offline, please check your internet connection.</PreHeader>
+
+<Router
+ {routes}
+ restoreScrollState={true}
+ on:routeLoading={() => {
+ document.getElementById("loader").style.display = "inline-block";
+ }}
+ on:routeLoaded={() => {
+ document.getElementById("loader").style.display = "none";
+ }}
+/>
diff --git a/apps/accounts/src/app/index.ts b/apps/accounts/src/app/index.ts
new file mode 100644
index 0000000..0bfb30d
--- /dev/null
+++ b/apps/accounts/src/app/index.ts
@@ -0,0 +1,14 @@
+import App from "./index.svelte";
+import "./index.scss";
+import {is_debug, is_development} from "$shared/lib/configuration";
+import {noop} from "$shared/lib/helpers";
+
+if (!is_development() && !is_debug()) {
+ console.log("%c Production; Suppressing logs", "background-color:yellow;color:black;font-size:18px;");
+ console.log = noop;
+}
+
+// @ts-ignore
+export default new App({
+ target: document.getElementById("root"),
+});
diff --git a/apps/accounts/src/app/pages/_layout.svelte b/apps/accounts/src/app/pages/_layout.svelte
new file mode 100644
index 0000000..8c2e4a8
--- /dev/null
+++ b/apps/accounts/src/app/pages/_layout.svelte
@@ -0,0 +1,142 @@
+<script>
+ import Details from "$shared/components/details.svelte";
+ import Button from "$shared/components/button.svelte";
+ import {switch_theme} from "$shared/lib/helpers";
+</script>
+
+<style>
+ #decoration {
+ position: absolute;
+ top: 0;
+ left: 0;
+ pointer-events: none;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ }
+
+ #decoration svg {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ -webkit-transform: translateX(-50%);
+ transform: translateX(-50%);
+ width: 134%;
+ min-width: 1280px;
+ max-width: 1920px;
+ height: auto;
+ }
+</style>
+
+<main class="container-fluid padding-x-xs padding-x-xxl@xs padding-y-md padding-y-lg@md max-width-sm">
+ <slot/>
+
+ <Details summary="About">
+ <p>Time Tracker is a tool to keep track of time spent.</p>
+ <p>Use demo@demo.demo 123456 to demo the app.</p>
+ <a href="https://git.ivarlovlie.no/time-tracker">Source</a>
+ <a href="https://git.ivarlovlie.no/time-tracker/tree/LICENSE">License</a>
+ <a href="/assets/third-party-licenses.txt">License notices</a>
+ </Details>
+
+ <Details summary="Pricing"/>
+
+ <Details summary="Privacy policy">
+ <h3>Information we collect</h3>
+ <p>We collect information you the user provide, explicitly this means:</p>
+ <ul>
+ <li>Username</li>
+ <li>Password</li>
+ <li>Entries generated by you</li>
+ <li>Labels generated by you</li>
+ <li>Categories generated by you</li>
+ <li>Your IP address when making requests to our API (using the service)</li>
+ </ul>
+
+ <h3>How we use your information</h3>
+ <p>We use your information to provide the time-tracker service.</p>
+
+ <h3>How we share your information</h3>
+ <p>
+ We do not share your information with anyone nor any entity. All information is handled by us the provider and you the user
+ exclusively.
+ </p>
+
+ <h3>Right to delete</h3>
+ <p>
+ You can at any time delete any data related to your personal information by navigating to your profile page inside of the
+ service.
+ </p>
+
+ <h3>Right to inspect</h3>
+ <p>You can at any time download all of your generated data by navigating to your profile page inside of the service.</p>
+
+ <h3>Contact</h3>
+ <p>Please direct any inquires about your personal data to time-tracker@ivarlovlie.no.</p>
+ </Details>
+
+ <Details summary="Terms of service"/>
+
+ <Button on:click={() => switch_theme()}
+ text="Switch theme"
+ variant="secondary"/>
+
+ <figure id="decoration"
+ aria-hidden="true">
+ <svg class="color-contrast-higher opacity-10%"
+ viewBox="0 0 1920 450"
+ fill="none">
+ <g stroke="currentColor"
+ stroke-width="2">
+ <rect x="1286"
+ y="64"
+ width="128"
+ height="128"/>
+ <circle cx="1350"
+ cy="128"
+ r="64"/>
+ <path d="M1286 64L1414 192"/>
+ <circle cx="1478"
+ cy="128"
+ r="64"/>
+ <rect x="1414"
+ y="192"
+ width="128"
+ height="128"/>
+ <circle cx="1478"
+ cy="256"
+ r="64"/>
+ <path d="M1414 192L1542 320"/>
+ <circle cx="1606"
+ cy="256"
+ r="64"/>
+ <rect x="1542"
+ y="320"
+ width="128"
+ height="128"/>
+ <circle cx="1606"
+ cy="384"
+ r="64"/>
+ <path d="M1542 320L1670 448"/>
+ <rect x="1690"
+ y="192"
+ width="128"
+ height="128"/>
+ <circle cx="1754"
+ cy="256"
+ r="64"/>
+ <path d="M1690 192L1818 320"/>
+ <rect x="1542"
+ y="64"
+ width="128"
+ height="128"/>
+ <circle cx="1606"
+ cy="128"
+ r="64"/>
+ <path d="M1542 64L1670 192"/>
+ <circle cx="1478"
+ r="64"/>
+ </g>
+ </svg>
+ </figure>
+</main>
diff --git a/apps/accounts/src/app/pages/forgot.svelte b/apps/accounts/src/app/pages/forgot.svelte
new file mode 100644
index 0000000..f22d664
--- /dev/null
+++ b/apps/accounts/src/app/pages/forgot.svelte
@@ -0,0 +1,99 @@
+<script>
+ import {onMount} from "svelte";
+ import {link} from "svelte-spa-router";
+ import {create_forgot_password_request} from "$shared/lib/api/user";
+ import {is_email} from "$shared/lib/helpers";
+ import Alert from "$shared/components/alert.svelte";
+ import Button from "$shared/components/button.svelte";
+ import Layout from "./_layout.svelte";
+
+ let isLoading = false;
+ let username;
+
+ const alert = {
+ title: "",
+ type: "",
+ message: "",
+ isVisible: false,
+ show(type, obj) {
+ alert.title = obj.title;
+ alert.message = obj.text;
+ alert.type = type;
+ alert.isVisible = true;
+ isLoading = false;
+ },
+ hide() {
+ alert.isVisible = false;
+ alert.title = "";
+ alert.message = "";
+ alert.type = "";
+ isLoading = false;
+ },
+ };
+
+ function is_valid() {
+ return is_email(username);
+ }
+
+ async function submit_form() {
+ if (isLoading) {
+ return;
+ }
+ if (is_valid()) {
+ isLoading = true;
+ const response = await create_forgot_password_request(username);
+ if (response.ok) {
+ alert.show("success", {
+ title: "Request is sent",
+ text: "If we find an account associated with this email address, you will receive an email with a reset link very soon.",
+ });
+ } else {
+ console.error(response.data);
+ alert.show("error", {
+ title: response.data?.title ?? "An error occured",
+ text: response.data?.text ?? "Please try again soon",
+ });
+ }
+ }
+ }
+
+ onMount(() => {
+ document.addEventListener("DOMContentLoaded", () => {
+ document.getElementById("email-address").focus();
+ });
+ });
+</script>
+
+<Layout>
+ <form on:submit|preventDefault={submit_form}
+ class="margin-bottom-md max-width-xxs">
+ <fieldset>
+ <legend class="form-legend">
+ <span class="margin-bottom-xs">Send reset link</span> <br/>
+ <span class="text-sm">... or <a href="/login"
+ use:link>log in</a></span>
+ </legend>
+ <div class="margin-bottom-xs">
+ <p>Provide your email address, and we'll send you a link to set your new password.</p>
+ </div>
+ <div class="margin-bottom-xxs max-width-xxs">
+ <Alert visible={alert.isVisible}
+ title={alert.title}
+ message={alert.message}
+ type={alert.type}/>
+ </div>
+ <div class="margin-bottom-xs">
+ <input type="email"
+ id="email-address"
+ placeholder="Email address"
+ class="form-control width-100%"
+ bind:value={username}/>
+ </div>
+ <div class="flex justify-end">
+ <Button text="Send reset link"
+ type="primary"
+ loading={isLoading}/>
+ </div>
+ </fieldset>
+ </form>
+</Layout>
diff --git a/apps/accounts/src/app/pages/login.svelte b/apps/accounts/src/app/pages/login.svelte
new file mode 100644
index 0000000..3324056
--- /dev/null
+++ b/apps/accounts/src/app/pages/login.svelte
@@ -0,0 +1,145 @@
+<script>
+ import {onMount} from "svelte";
+ import {link, querystring} from "svelte-spa-router";
+ import {api_base, projects_base, IconNames} from "$shared/lib/configuration";
+ import Button from "$shared/components/button.svelte";
+ import Alert from "$shared/components/alert.svelte";
+ import {login} from "$shared/lib/api/user";
+ import {is_email} from "$shared/lib/helpers";
+ import Layout from "./_layout.svelte";
+
+ const loginForm = {
+ loading: false,
+ values: {
+ username: "",
+ password: "",
+ },
+ alert: {
+ title: "",
+ type: "",
+ message: "",
+ isVisible: false,
+ show(type, obj) {
+ loginForm.alert.title = obj.title;
+ loginForm.alert.message = obj.text;
+ loginForm.alert.type = type;
+ loginForm.alert.isVisible = true;
+ loginForm.loading = false;
+ },
+ hide() {
+ loginForm.alert.isVisible = false;
+ loginForm.alert.title = "";
+ loginForm.alert.message = "";
+ loginForm.alert.type = "";
+ },
+ },
+ is_valid() {
+ return is_email(loginForm.values.username) && loginForm.values.password.length > 0;
+ },
+ async submit_form() {
+ if (loginForm.loading) {
+ return;
+ }
+ if (loginForm.is_valid()) {
+ loginForm.alert.hide();
+ loginForm.loading = true;
+ try {
+ const response = await login(loginForm.values);
+ if (response.ok) {
+ location.replace(projects_base("#/home"));
+ } else {
+ if (response.data.title || response.data.text) {
+ loginForm.alert.show("error", {
+ title: response.data.title ?? "",
+ text: response.data.text ?? "",
+ });
+ } else {
+ loginForm.alert.show("error", {
+ title: "An unknown error occured",
+ text: "Try again soon",
+ });
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ loginForm.alert.show("error", {
+ title: "An error occured",
+ text: "Could not connect to server, please check your internet connection",
+ });
+ }
+ } else {
+ loginForm.alert.show("error", {
+ title: "Invalid form",
+ });
+ }
+ },
+ };
+
+ onMount(() => {
+ if ($querystring === "deleted") {
+ loginForm.alert.show("info", {
+ title: "Account deleted",
+ text: "Your account and all its data was successfully deleted.",
+ });
+ }
+ if ($querystring === "expired") {
+ loginForm.alert.show("info", {
+ title: "Session expired",
+ text: "Your session has expired, feel free to log in again.",
+ });
+ }
+ });
+</script>
+
+<Layout>
+ <form on:submit|preventDefault={loginForm.submit_form}
+ class="margin-bottom-md max-width-xxs">
+ <fieldset>
+ <legend class="form-legend">
+ <span class="margin-bottom-xs">Log into your account</span>
+ <br/>
+ <span class="text-sm">... or <a href="/signup"
+ use:link>create a new one</a></span>
+ </legend>
+ <div class="margin-bottom-xxs max-width-xxs">
+ <Alert visible={loginForm.alert.isVisible}
+ title={loginForm.alert.title}
+ message={loginForm.alert.message}
+ type={loginForm.alert.type}/>
+ </div>
+ <div class="margin-bottom-xxs">
+ <input type="email"
+ placeholder="Email address"
+ class="form-control width-100%"
+ id="username"
+ bind:value={loginForm.values.username}/>
+ </div>
+ <div class="margin-bottom-xxs">
+ <input type="password"
+ placeholder="Password"
+ id="password"
+ class="form-control width-100%"
+ bind:value={loginForm.values.password}/>
+ <div class="flex justify-end">
+ <a tabindex="-1"
+ class="text-sm"
+ href="/forgot"
+ use:link>Reset password</a>
+ </div>
+ </div>
+ <div class="flex justify-between">
+ <Button text="Login with Github"
+ variant="secondary"
+ icon="{IconNames.github}"
+ icon_right_aligned="true"
+ href={api_base("_/account/create-github-session")}
+ loading={loginForm.loading}
+ />
+ <Button text="Login"
+ type="submit"
+ variant="primary"
+ loading={loginForm.loading}/>
+ </div>
+ </fieldset>
+ </form>
+</Layout>
diff --git a/apps/accounts/src/app/pages/not-found.svelte b/apps/accounts/src/app/pages/not-found.svelte
new file mode 100644
index 0000000..34568ba
--- /dev/null
+++ b/apps/accounts/src/app/pages/not-found.svelte
@@ -0,0 +1,23 @@
+<script>
+ import {link} from "svelte-spa-router";
+</script>
+<style>
+ header {
+ font-size: 12rem;
+ }
+
+ main {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ }
+</style>
+
+<main>
+ <header>404</header>
+ <p>Page not found!</p>
+ <a use:link
+ href="/">Go to front</a>
+</main>
diff --git a/apps/accounts/src/app/pages/reset-password.svelte b/apps/accounts/src/app/pages/reset-password.svelte
new file mode 100644
index 0000000..56c4f62
--- /dev/null
+++ b/apps/accounts/src/app/pages/reset-password.svelte
@@ -0,0 +1,135 @@
+<script>
+ import {querystring, link} from "svelte-spa-router";
+ import {check_forgot_password_request, fulfill_forgot_password_request} from "$shared/lib/api/user";
+ import Alert from "$shared/components/alert.svelte";
+ import Button from "$shared/components/button.svelte";
+ import Layout from "./_layout.svelte";
+
+ const requestId = new URLSearchParams($querystring).get("id");
+ let isLoading = false;
+ let newPassword;
+ let newPasswordError;
+ let alert = {
+ title: "",
+ type: "",
+ message: "",
+ isVisible: false,
+ show(type, obj) {
+ alert.title = obj.title;
+ alert.message = obj.text;
+ alert.type = type;
+ alert.isVisible = true;
+ isLoading = false;
+ },
+ hide() {
+ alert.isVisible = false;
+ alert.title = "";
+ alert.message = "";
+ alert.type = "";
+ isLoading = false;
+ },
+ };
+
+ function is_valid() {
+ let isValid = true;
+ if (!newPassword.length > 5) {
+ newPasswordError = "The new password must be at least 5 characters";
+ isValid = false;
+ }
+ return isValid;
+ }
+
+ async function submit() {
+ if (isLoading) {
+ return;
+ }
+ if (is_valid()) {
+ isLoading = true;
+ const response = await fulfill_forgot_password_request(requestId, newPassword);
+ if (response.ok) {
+ alert.show("success", {
+ title: "Your new password is set",
+ text: "<a href='/#/login'>Click here to log in</a>",
+ });
+ } else {
+ console.error(response.data);
+ alert.show("error", {
+ title: response.data?.title ?? "An error occured",
+ text: response.data?.text ?? "Please try again soon",
+ });
+ }
+ }
+ }
+
+ async function is_valid_password_reset_request() {
+ const response = await check_forgot_password_request(requestId);
+ if (response.ok) {
+ return response.data === true;
+ }
+ return false;
+ }
+</script>
+
+<Layout>
+ <form on:submit|preventDefault={submit}
+ class="margin-bottom-md max-width-xxs {isLoading ? 'c-disabled loading' : ''}">
+ {#if requestId}
+ {#await is_valid_password_reset_request()}
+ <p>Checking your request...</p>
+ <a href="/login"
+ use:link>cancel</a>
+ {:then isActive}
+ {#if isActive === true}
+ <fieldset>
+ <legend class="form-legend">
+ <span class="margin-bottom-xs">Set your new password</span> <br/>
+ <span class="text-sm">
+ ... or
+ <a href="/login"
+ use:link> log in </a>
+ </span>
+ </legend>
+ <div class="margin-bottom-xxs max-width-xxs">
+ <Alert visible={alert.isVisible}
+ title={alert.title}
+ message={alert.message}
+ type={alert.type}/>
+ </div>
+ <div class="margin-bottom-xs">
+ <input
+ type="password"
+ id="new-password"
+ placeholder="New password"
+ class="form-control width-100%"
+ bind:value={newPassword}
+ />
+ {#if newPasswordError}
+ <small class="color-danger">{newPasswordError}</small>
+ {/if}
+ </div>
+ <div class="flex justify-end">
+ <Button text="Set new password"
+ type="primary"
+ loading={isLoading}
+ on:click={submit}/>
+ </div>
+ </fieldset>
+ {:else}
+ <Alert title="This request is expired"
+ message="Please submit the forgot password form again"
+ type="warning"/>
+ <div class="flex justify-between width-100% margin-y-sm">
+ <a href="/forgot"
+ use:link>Go to forgot form</a>
+ <a href="/login"
+ use:link>Go to login form</a>
+ </div>
+ {/if}
+ {:catch _}
+ <Alert title="An error occured"
+ message="Please try again soon"
+ type="error"/>
+ {/await}
+ {/if}
+ </form>
+</Layout>
diff --git a/apps/accounts/src/app/pages/sign-up.svelte b/apps/accounts/src/app/pages/sign-up.svelte
new file mode 100644
index 0000000..80780e0
--- /dev/null
+++ b/apps/accounts/src/app/pages/sign-up.svelte
@@ -0,0 +1,128 @@
+<script>
+ import {create_account} from "$shared/lib/api/user";
+ import {is_email} from "$shared/lib/helpers";
+ import Alert from "$shared/components/alert.svelte";
+ import Button from "$shared/components/button.svelte";
+ import {link} from "svelte-spa-router";
+ import Layout from "./_layout.svelte";
+
+ const signupForm = {
+ loading: false,
+ values: {
+ username: "",
+ password: "",
+ },
+ alert: {
+ title: "",
+ type: "",
+ message: "",
+ isVisible: false,
+ show(type, obj) {
+ signupForm.alert.title = obj.title;
+ signupForm.alert.message = obj.text;
+ signupForm.alert.type = type;
+ signupForm.alert.isVisible = true;
+ signupForm.loading = false;
+ },
+ hide() {
+ signupForm.alert.isVisible = false;
+ signupForm.alert.title = "";
+ signupForm.alert.message = "";
+ signupForm.alert.type = "";
+ },
+ },
+ is_valid() {
+ return (
+ is_email(signupForm.values.username) &&
+ signupForm.values.password.length > 0
+ );
+ },
+ async submit_form() {
+ if (signupForm.loading) {
+ return;
+ }
+ if (signupForm.is_valid()) {
+ signupForm.alert.hide();
+ signupForm.loading = true;
+ try {
+ const response = await create_account(signupForm.values);
+ if (response.ok) {
+ location.reload();
+ } else {
+ if (response.data.title || response.data.text) {
+ signupForm.alert.show("error", {
+ title: response.data.title ?? "",
+ text: response.data.text ?? "",
+ });
+ } else {
+ signupForm.alert.show("error", {
+ title: "An unknown error occured",
+ text: "Try again soon",
+ });
+ }
+ }
+ } catch (e) {
+ console.error(e);
+ signupForm.alert.show("error", {
+ title: "An error occured",
+ text: "Could not connect to server, please check your internet connection",
+ });
+ }
+ } else {
+ signupForm.alert.show("error", {
+ title: "Invalid form",
+ });
+ }
+ },
+ };
+</script>
+
+<Layout>
+ <form
+ on:submit|preventDefault={signupForm.submit_form}
+ class="margin-bottom-md max-width-xxs"
+ >
+ <fieldset>
+ <legend class="form-legend">
+ <span class="margin-bottom-xs">Create your account</span> <br/>
+ <span class="text-sm"
+ >... or <a href="/login"
+ use:link>log in</a></span
+ >
+ </legend>
+ <div class="margin-bottom-xxs max-width-xxs">
+ <Alert
+ visible={signupForm.alert.isVisible}
+ title={signupForm.alert.title}
+ message={signupForm.alert.message}
+ type={signupForm.alert.type}
+ />
+ </div>
+ <div class="margin-bottom-xxs">
+ <input
+ type="email"
+ placeholder="Email address"
+ class="form-control width-100%"
+ id="email-address"
+ bind:value={signupForm.values.username}
+ />
+ </div>
+ <div class="margin-bottom-xxs">
+ <input
+ type="password"
+ placeholder="Password"
+ class="form-control width-100%"
+ bind:value={signupForm.values.password}
+ />
+ </div>
+ <div class="flex justify-end">
+ <Button
+ class="margin-bottom-xs"
+ text="Submit"
+ type="primary"
+ loading={signupForm.loading}
+ />
+ </div>
+ </fieldset>
+ </form>
+</Layout>
diff --git a/apps/accounts/src/index.html b/apps/accounts/src/index.html
new file mode 100644
index 0000000..985b62b
--- /dev/null
+++ b/apps/accounts/src/index.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html lang="en">
+
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
+ <link rel="apple-touch-icon"
+ sizes="180x180"
+ href="./_assets/pwa/apple-touch-icon.png">
+ <link rel="icon"
+ type="image/png"
+ sizes="32x32"
+ href="./_assets/pwa/favicon-32x32.png">
+ <link rel="icon"
+ type="image/png"
+ sizes="16x16"
+ href="./_assets/pwa/favicon-16x16.png">
+ <link rel="manifest"
+ href="./_assets/pwa/manifest.json">
+ <link rel="mask-icon"
+ href="./_assets/pwa/safari-pinned-tab.svg"
+ color="#5bbad5">
+ <meta name="msapplication-TileColor"
+ content="#da532c">
+ <link rel="icon"
+ href="./_assets/pwa/favicon.svg">
+ <script>
+ const currentTheme = localStorage.getItem("theme");
+ if (currentTheme === "light") {
+ document.querySelector("html").dataset.theme = "light";
+ } else {
+ document.querySelector("html").dataset.theme = "dark";
+ }
+ </script>
+ <link rel="stylesheet"
+ href="./_assets/pre.css">
+ <title>Time Tracker</title>
+</head>
+
+<body>
+
+<noscript>
+ This page is built with javascript. Allow it and try again.
+</noscript>
+
+<div class="fill-loader fill-loader--v4"
+ id="loader"
+ role="alert">
+ <p class="fill-loader__label">Loading Time Tracker...</p>
+ <div aria-hidden="true">
+ <div class="fill-loader__base"></div>
+ <div class="fill-loader__fill"></div>
+ </div>
+</div>
+
+<div id="root"></div>
+
+<script type="module"
+ src="./app/index.ts"></script>
+</body>
+
+</html>
diff --git a/apps/accounts/src/package.json b/apps/accounts/src/package.json
new file mode 100644
index 0000000..8ff516d
--- /dev/null
+++ b/apps/accounts/src/package.json
@@ -0,0 +1,22 @@
+{
+ "name": "time-tracker-public",
+ "version": "0.0.1",
+ "private": "true",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build"
+ },
+ "devDependencies": {
+ "@sveltejs/vite-plugin-svelte": "1.0.0-next.43",
+ "sass": "^1.51.0",
+ "svelte": "^3.48.0",
+ "svelte-preprocess": "^4.10.6",
+ "svelte-spa-router": "^3.2.0",
+ "typescript": "4.6.4",
+ "vite": "^2.9.8"
+ },
+ "dependencies": {
+ "@js-temporal/polyfill": "^0.4.1",
+ "fuzzysort": "^1.9.0"
+ }
+}
diff --git a/apps/accounts/src/pnpm-lock.yaml b/apps/accounts/src/pnpm-lock.yaml
new file mode 100644
index 0000000..3b56115
--- /dev/null
+++ b/apps/accounts/src/pnpm-lock.yaml
@@ -0,0 +1,769 @@
+lockfileVersion: 5.4
+
+specifiers:
+ '@js-temporal/polyfill': ^0.4.1
+ '@sveltejs/vite-plugin-svelte': 1.0.0-next.43
+ fuzzysort: ^1.9.0
+ sass: ^1.51.0
+ svelte: ^3.48.0
+ svelte-preprocess: ^4.10.6
+ svelte-spa-router: ^3.2.0
+ typescript: 4.6.4
+ vite: ^2.9.8
+
+dependencies:
+ '@js-temporal/polyfill': 0.4.1
+ fuzzysort: 1.9.0
+
+devDependencies:
+ '@sveltejs/vite-plugin-svelte': 1.0.0-next.43_svelte@3.48.0+vite@2.9.8
+ sass: 1.51.0
+ svelte: 3.48.0
+ svelte-preprocess: 4.10.6_24ezlekk4ocevlsjgs2qnqmjum
+ svelte-spa-router: 3.2.0
+ typescript: 4.6.4
+ vite: 2.9.8_sass@1.51.0
+
+packages:
+
+ /@js-temporal/polyfill/0.4.1:
+ resolution: {integrity: sha512-q45ecIocpa2TLem2jNOsCrDwP/sgKZdSkt+C1Rx07OkdKsdbvVfHcD1iDiK9scxBZrBQ38uJ8VQISXBS70ql1w==}
+ engines: {node: '>=12'}
+ dependencies:
+ jsbi: 4.3.0
+ tslib: 2.4.0
+ dev: false
+
+ /@rollup/pluginutils/4.2.1:
+ resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
+ engines: {node: '>= 8.0.0'}
+ dependencies:
+ estree-walker: 2.0.2
+ picomatch: 2.3.1
+ dev: true
+
+ /@sveltejs/vite-plugin-svelte/1.0.0-next.43_svelte@3.48.0+vite@2.9.8:
+ resolution: {integrity: sha512-MzeczqGrnDmbAldw/LfXV/dhpLC2bdUzuMhcx0C2j79V2uNzQERHDinxXnG2AVTCTjSpbQxzQwMMmYflnI7W1g==}
+ engines: {node: ^14.13.1 || >= 16}
+ peerDependencies:
+ diff-match-patch: ^1.0.5
+ svelte: ^3.44.0
+ vite: ^2.9.0
+ peerDependenciesMeta:
+ diff-match-patch:
+ optional: true
+ dependencies:
+ '@rollup/pluginutils': 4.2.1
+ debug: 4.3.4
+ deepmerge: 4.2.2
+ kleur: 4.1.4
+ magic-string: 0.26.1
+ svelte: 3.48.0
+ svelte-hmr: 0.14.11_svelte@3.48.0
+ vite: 2.9.8_sass@1.51.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@types/node/17.0.31:
+ resolution: {integrity: sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q==}
+ dev: true
+
+ /@types/pug/2.0.6:
+ resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
+ dev: true
+
+ /@types/sass/1.43.1:
+ resolution: {integrity: sha512-BPdoIt1lfJ6B7rw35ncdwBZrAssjcwzI5LByIrYs+tpXlj/CAkuVdRsgZDdP4lq5EjyWzwxZCqAoFyHKFwp32g==}
+ dependencies:
+ '@types/node': 17.0.31
+ dev: true
+
+ /anymatch/3.1.2:
+ resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
+ engines: {node: '>= 8'}
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+ dev: true
+
+ /balanced-match/1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ dev: true
+
+ /binary-extensions/2.2.0:
+ resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /brace-expansion/1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+ dev: true
+
+ /braces/3.0.2:
+ resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+ engines: {node: '>=8'}
+ dependencies:
+ fill-range: 7.0.1
+ dev: true
+
+ /buffer-crc32/0.2.13:
+ resolution: {integrity: sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=}
+ dev: true
+
+ /chokidar/3.5.3:
+ resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+ engines: {node: '>= 8.10.0'}
+ dependencies:
+ anymatch: 3.1.2
+ braces: 3.0.2
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
+ /concat-map/0.0.1:
+ resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
+ dev: true
+
+ /debug/4.3.4:
+ resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ dependencies:
+ ms: 2.1.2
+ dev: true
+
+ /deepmerge/4.2.2:
+ resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /detect-indent/6.1.0:
+ resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /es6-promise/3.3.1:
+ resolution: {integrity: sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=}
+ dev: true
+
+ /esbuild-android-64/0.14.38:
+ resolution: {integrity: sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-android-arm64/0.14.38:
+ resolution: {integrity: sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-darwin-64/0.14.38:
+ resolution: {integrity: sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-darwin-arm64/0.14.38:
+ resolution: {integrity: sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-freebsd-64/0.14.38:
+ resolution: {integrity: sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-freebsd-arm64/0.14.38:
+ resolution: {integrity: sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-32/0.14.38:
+ resolution: {integrity: sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-64/0.14.38:
+ resolution: {integrity: sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-arm/0.14.38:
+ resolution: {integrity: sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-arm64/0.14.38:
+ resolution: {integrity: sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-mips64le/0.14.38:
+ resolution: {integrity: sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-ppc64le/0.14.38:
+ resolution: {integrity: sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-riscv64/0.14.38:
+ resolution: {integrity: sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-linux-s390x/0.14.38:
+ resolution: {integrity: sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-netbsd-64/0.14.38:
+ resolution: {integrity: sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-openbsd-64/0.14.38:
+ resolution: {integrity: sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-sunos-64/0.14.38:
+ resolution: {integrity: sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-windows-32/0.14.38:
+ resolution: {integrity: sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-windows-64/0.14.38:
+ resolution: {integrity: sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild-windows-arm64/0.14.38:
+ resolution: {integrity: sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /esbuild/0.14.38:
+ resolution: {integrity: sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==}
+ engines: {node: '>=12'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ esbuild-android-64: 0.14.38
+ esbuild-android-arm64: 0.14.38
+ esbuild-darwin-64: 0.14.38
+ esbuild-darwin-arm64: 0.14.38
+ esbuild-freebsd-64: 0.14.38
+ esbuild-freebsd-arm64: 0.14.38
+ esbuild-linux-32: 0.14.38
+ esbuild-linux-64: 0.14.38
+ esbuild-linux-arm: 0.14.38
+ esbuild-linux-arm64: 0.14.38
+ esbuild-linux-mips64le: 0.14.38
+ esbuild-linux-ppc64le: 0.14.38
+ esbuild-linux-riscv64: 0.14.38
+ esbuild-linux-s390x: 0.14.38
+ esbuild-netbsd-64: 0.14.38
+ esbuild-openbsd-64: 0.14.38
+ esbuild-sunos-64: 0.14.38
+ esbuild-windows-32: 0.14.38
+ esbuild-windows-64: 0.14.38
+ esbuild-windows-arm64: 0.14.38
+ dev: true
+
+ /estree-walker/2.0.2:
+ resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+ dev: true
+
+ /fill-range/7.0.1:
+ resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ to-regex-range: 5.0.1
+ dev: true
+
+ /fs.realpath/1.0.0:
+ resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=}
+ dev: true
+
+ /fsevents/2.3.2:
+ resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /function-bind/1.1.1:
+ resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+ dev: true
+
+ /fuzzysort/1.9.0:
+ resolution: {integrity: sha512-MOxCT0qLTwLqmEwc7UtU045RKef7mc8Qz8eR4r2bLNEq9dy/c3ZKMEFp6IEst69otkQdFZ4FfgH2dmZD+ddX1g==}
+ dev: false
+
+ /glob-parent/5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+ dependencies:
+ is-glob: 4.0.3
+ dev: true
+
+ /glob/7.2.0:
+ resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==}
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+ dev: true
+
+ /graceful-fs/4.2.10:
+ resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
+ dev: true
+
+ /has/1.0.3:
+ resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+ engines: {node: '>= 0.4.0'}
+ dependencies:
+ function-bind: 1.1.1
+ dev: true
+
+ /immutable/4.0.0:
+ resolution: {integrity: sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==}
+ dev: true
+
+ /inflight/1.0.6:
+ resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=}
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+ dev: true
+
+ /inherits/2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ dev: true
+
+ /is-binary-path/2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+ dependencies:
+ binary-extensions: 2.2.0
+ dev: true
+
+ /is-core-module/2.9.0:
+ resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==}
+ dependencies:
+ has: 1.0.3
+ dev: true
+
+ /is-extglob/2.1.1:
+ resolution: {integrity: sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-glob/4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ is-extglob: 2.1.1
+ dev: true
+
+ /is-number/7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+ dev: true
+
+ /jsbi/4.3.0:
+ resolution: {integrity: sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g==}
+ dev: false
+
+ /kleur/4.1.4:
+ resolution: {integrity: sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /magic-string/0.25.9:
+ resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
+ dependencies:
+ sourcemap-codec: 1.4.8
+ dev: true
+
+ /magic-string/0.26.1:
+ resolution: {integrity: sha512-ndThHmvgtieXe8J/VGPjG+Apu7v7ItcD5mhEIvOscWjPF/ccOiLxHaSuCAS2G+3x4GKsAbT8u7zdyamupui8Tg==}
+ engines: {node: '>=12'}
+ dependencies:
+ sourcemap-codec: 1.4.8
+ dev: true
+
+ /min-indent/1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /minimatch/3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ dependencies:
+ brace-expansion: 1.1.11
+ dev: true
+
+ /minimist/1.2.6:
+ resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
+ dev: true
+
+ /mkdirp/0.5.6:
+ resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+ hasBin: true
+ dependencies:
+ minimist: 1.2.6
+ dev: true
+
+ /ms/2.1.2:
+ resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+ dev: true
+
+ /nanoid/3.3.4:
+ resolution: {integrity: sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+ dev: true
+
+ /normalize-path/3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /once/1.4.0:
+ resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
+ dependencies:
+ wrappy: 1.0.2
+ dev: true
+
+ /path-is-absolute/1.0.1:
+ resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /path-parse/1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+ dev: true
+
+ /picocolors/1.0.0:
+ resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+ dev: true
+
+ /picomatch/2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+ dev: true
+
+ /postcss/8.4.13:
+ resolution: {integrity: sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.4
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+ dev: true
+
+ /readdirp/3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+ dependencies:
+ picomatch: 2.3.1
+ dev: true
+
+ /regexparam/2.0.0:
+ resolution: {integrity: sha512-gJKwd2MVPWHAIFLsaYDZfyKzHNS4o7E/v8YmNf44vmeV2e4YfVoDToTOKTvE7ab68cRJ++kLuEXJBaEeJVt5ow==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /resolve/1.22.0:
+ resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
+ hasBin: true
+ dependencies:
+ is-core-module: 2.9.0
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+ dev: true
+
+ /rimraf/2.7.1:
+ resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+ hasBin: true
+ dependencies:
+ glob: 7.2.0
+ dev: true
+
+ /rollup/2.72.1:
+ resolution: {integrity: sha512-NTc5UGy/NWFGpSqF1lFY8z9Adri6uhyMLI6LvPAXdBKoPRFhIIiBUpt+Qg2awixqO3xvzSijjhnb4+QEZwJmxA==}
+ engines: {node: '>=10.0.0'}
+ hasBin: true
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
+ /sander/0.5.1:
+ resolution: {integrity: sha1-dB4kXiMfB8r7b98PEzrfohalAq0=}
+ dependencies:
+ es6-promise: 3.3.1
+ graceful-fs: 4.2.10
+ mkdirp: 0.5.6
+ rimraf: 2.7.1
+ dev: true
+
+ /sass/1.51.0:
+ resolution: {integrity: sha512-haGdpTgywJTvHC2b91GSq+clTKGbtkkZmVAb82jZQN/wTy6qs8DdFm2lhEQbEwrY0QDRgSQ3xDurqM977C3noA==}
+ engines: {node: '>=12.0.0'}
+ hasBin: true
+ dependencies:
+ chokidar: 3.5.3
+ immutable: 4.0.0
+ source-map-js: 1.0.2
+ dev: true
+
+ /sorcery/0.10.0:
+ resolution: {integrity: sha1-iukK19fLBfxZ8asMY3hF1cFaUrc=}
+ hasBin: true
+ dependencies:
+ buffer-crc32: 0.2.13
+ minimist: 1.2.6
+ sander: 0.5.1
+ sourcemap-codec: 1.4.8
+ dev: true
+
+ /source-map-js/1.0.2:
+ resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /sourcemap-codec/1.4.8:
+ resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
+ dev: true
+
+ /strip-indent/3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ min-indent: 1.0.1
+ dev: true
+
+ /supports-preserve-symlinks-flag/1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+ dev: true
+
+ /svelte-hmr/0.14.11_svelte@3.48.0:
+ resolution: {integrity: sha512-R9CVfX6DXxW1Kn45Jtmx+yUe+sPhrbYSUp7TkzbW0jI5fVPn6lsNG9NEs5dFg5qRhFNAoVdRw5qQDLALNKhwbQ==}
+ engines: {node: ^12.20 || ^14.13.1 || >= 16}
+ peerDependencies:
+ svelte: '>=3.19.0'
+ dependencies:
+ svelte: 3.48.0
+ dev: true
+
+ /svelte-preprocess/4.10.6_24ezlekk4ocevlsjgs2qnqmjum:
+ resolution: {integrity: sha512-I2SV1w/AveMvgIQlUF/ZOO3PYVnhxfcpNyGt8pxpUVhPfyfL/CZBkkw/KPfuFix5FJ9TnnNYMhACK3DtSaYVVQ==}
+ engines: {node: '>= 9.11.2'}
+ requiresBuild: true
+ peerDependencies:
+ '@babel/core': ^7.10.2
+ coffeescript: ^2.5.1
+ less: ^3.11.3 || ^4.0.0
+ node-sass: '*'
+ postcss: ^7 || ^8
+ postcss-load-config: ^2.1.0 || ^3.0.0
+ pug: ^3.0.0
+ sass: ^1.26.8
+ stylus: ^0.55.0
+ sugarss: ^2.0.0
+ svelte: ^3.23.0
+ typescript: ^3.9.5 || ^4.0.0
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ coffeescript:
+ optional: true
+ less:
+ optional: true
+ node-sass:
+ optional: true
+ postcss:
+ optional: true
+ postcss-load-config:
+ optional: true
+ pug:
+ optional: true
+ sass:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ typescript:
+ optional: true
+ dependencies:
+ '@types/pug': 2.0.6
+ '@types/sass': 1.43.1
+ detect-indent: 6.1.0
+ magic-string: 0.25.9
+ sass: 1.51.0
+ sorcery: 0.10.0
+ strip-indent: 3.0.0
+ svelte: 3.48.0
+ typescript: 4.6.4
+ dev: true
+
+ /svelte-spa-router/3.2.0:
+ resolution: {integrity: sha512-igemo5Vs82TGBBw+DjWt6qKameXYzNs6aDXcTxou5XbEvOjiRcAM6MLkdVRCatn6u8r42dE99bt/br7T4qe/AQ==}
+ dependencies:
+ regexparam: 2.0.0
+ dev: true
+
+ /svelte/3.48.0:
+ resolution: {integrity: sha512-fN2YRm/bGumvjUpu6yI3BpvZnpIm9I6A7HR4oUNYd7ggYyIwSA/BX7DJ+UXXffLp6XNcUijyLvttbPVCYa/3xQ==}
+ engines: {node: '>= 8'}
+ dev: true
+
+ /to-regex-range/5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+ dependencies:
+ is-number: 7.0.0
+ dev: true
+
+ /tslib/2.4.0:
+ resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==}
+ dev: false
+
+ /typescript/4.6.4:
+ resolution: {integrity: sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==}
+ engines: {node: '>=4.2.0'}
+ hasBin: true
+ dev: true
+
+ /vite/2.9.8_sass@1.51.0:
+ resolution: {integrity: sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==}
+ engines: {node: '>=12.2.0'}
+ hasBin: true
+ peerDependencies:
+ less: '*'
+ sass: '*'
+ stylus: '*'
+ peerDependenciesMeta:
+ less:
+ optional: true
+ sass:
+ optional: true
+ stylus:
+ optional: true
+ dependencies:
+ esbuild: 0.14.38
+ postcss: 8.4.13
+ resolve: 1.22.0
+ rollup: 2.72.1
+ sass: 1.51.0
+ optionalDependencies:
+ fsevents: 2.3.2
+ dev: true
+
+ /wrappy/1.0.2:
+ resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
+ dev: true
diff --git a/apps/accounts/src/tsconfig.json b/apps/accounts/src/tsconfig.json
new file mode 100644
index 0000000..e00d638
--- /dev/null
+++ b/apps/accounts/src/tsconfig.json
@@ -0,0 +1,30 @@
+{
+ "include": [
+ "./**/*.d.ts",
+ "./**/*.ts",
+ "./**/*.js",
+ "./**/*.svelte"
+ ],
+ "exclude": [
+ "./node_modules"
+ ],
+ "compilerOptions": {
+ "target": "esnext",
+ "useDefineForClassFields": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "allowJs": true,
+ "checkJs": false,
+ "paths": {
+ "$app/*": [
+ "./_public/*"
+ ],
+ "$app/*": [
+ "./app/*"
+ ],
+ "$shared/*": [
+ "../../web-shared/src/*"
+ ]
+ }
+ }
+}
diff --git a/apps/accounts/src/vite.config.ts b/apps/accounts/src/vite.config.ts
new file mode 100644
index 0000000..907422e
--- /dev/null
+++ b/apps/accounts/src/vite.config.ts
@@ -0,0 +1,30 @@
+import {defineConfig} from "vite";
+import {svelte} from "@sveltejs/vite-plugin-svelte";
+import sveltePreprocess from "svelte-preprocess";
+// @ts-ignore
+import path from "path";
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ resolve: {
+ alias: {
+ "$shared": path.resolve(__dirname, "../../web-shared/src"),
+ "$app": path.resolve(__dirname, "./app"),
+ }
+ },
+ build: {
+ outDir: "build",
+ emptyOutDir: true,
+ rollupOptions: {
+ input: {
+ main: path.resolve(__dirname, "index.html"),
+ }
+ }
+ },
+
+ plugins: [
+ svelte({
+ preprocess: sveltePreprocess()
+ })
+ ],
+});