summaryrefslogtreecommitdiffstats
path: root/apps/projects-web
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2022-06-01 22:20:31 +0200
committerivarlovlie <git@ivarlovlie.no>2022-06-01 22:20:31 +0200
commit9db0f70f75886ab1f97e231a3f9c16fcbbbe04bd (patch)
treeee4386cdffe27eaf0325b4617c7fafefbb9f7d51 /apps/projects-web
parent7058f55fe099d6b6dea50558f30a4dc8ee52dde8 (diff)
downloadgreatoffice-9db0f70f75886ab1f97e231a3f9c16fcbbbe04bd.tar.xz
greatoffice-9db0f70f75886ab1f97e231a3f9c16fcbbbe04bd.zip
refactor: Move projects-web to projects
Diffstat (limited to 'apps/projects-web')
-rw-r--r--apps/projects-web/.version1
-rw-r--r--apps/projects-web/.version-dev1
-rw-r--r--apps/projects-web/CHANGELOG.md67
-rwxr-xr-xapps/projects-web/build_and_push.sh76
-rw-r--r--apps/projects-web/cliff.toml62
-rw-r--r--apps/projects-web/src/_assets/pre.css128
-rw-r--r--apps/projects-web/src/_assets/pwa/android-chrome-192x192.pngbin3291 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/android-chrome-512x512.pngbin9687 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/apple-touch-icon.pngbin2769 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/browserconfig.xml9
-rw-r--r--apps/projects-web/src/_assets/pwa/favicon-16x16.pngbin636 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/favicon-32x32.pngbin907 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/favicon.icobin15086 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/favicon.svg4
-rw-r--r--apps/projects-web/src/_assets/pwa/manifest.json28
-rw-r--r--apps/projects-web/src/_assets/pwa/mstile-144x144.pngbin3109 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/mstile-150x150.pngbin3238 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/mstile-310x150.pngbin3501 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/mstile-310x310.pngbin6823 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/mstile-70x70.pngbin2238 -> 0 bytes
-rw-r--r--apps/projects-web/src/_assets/pwa/safari-pinned-tab.svg50
-rw-r--r--apps/projects-web/src/app/index.d.ts48
-rw-r--r--apps/projects-web/src/app/index.html63
-rw-r--r--apps/projects-web/src/app/index.scss38
-rw-r--r--apps/projects-web/src/app/index.svelte56
-rw-r--r--apps/projects-web/src/app/index.ts16
-rw-r--r--apps/projects-web/src/app/lib/services/user-service.ts21
-rw-r--r--apps/projects-web/src/app/lib/stores/categories.ts44
-rw-r--r--apps/projects-web/src/app/lib/stores/entries.ts74
-rw-r--r--apps/projects-web/src/app/lib/stores/labels.ts44
-rw-r--r--apps/projects-web/src/app/pages/_layout.svelte79
-rw-r--r--apps/projects-web/src/app/pages/data.svelte392
-rw-r--r--apps/projects-web/src/app/pages/home.svelte167
-rw-r--r--apps/projects-web/src/app/pages/not-found.svelte24
-rw-r--r--apps/projects-web/src/app/pages/settings.svelte12
-rw-r--r--apps/projects-web/src/app/pages/ui-workbench.svelte48
-rw-r--r--apps/projects-web/src/app/pages/views/category-form/index.svelte144
-rw-r--r--apps/projects-web/src/app/pages/views/data-table-paginator.svelte107
-rw-r--r--apps/projects-web/src/app/pages/views/entry-form/index.svelte196
-rw-r--r--apps/projects-web/src/app/pages/views/entry-form/sections/category.svelte75
-rw-r--r--apps/projects-web/src/app/pages/views/entry-form/sections/date-time.svelte165
-rw-r--r--apps/projects-web/src/app/pages/views/entry-form/sections/labels.svelte65
-rw-r--r--apps/projects-web/src/app/pages/views/profile-modal.svelte156
-rw-r--r--apps/projects-web/src/app/pages/views/settings-categories-tile.svelte127
-rw-r--r--apps/projects-web/src/app/pages/views/settings-labels-tile.svelte112
-rw-r--r--apps/projects-web/src/index.html63
-rw-r--r--apps/projects-web/src/package.json22
-rw-r--r--apps/projects-web/src/pnpm-lock.yaml769
-rw-r--r--apps/projects-web/src/tsconfig.json27
-rw-r--r--apps/projects-web/src/vite.config.ts31
50 files changed, 0 insertions, 3611 deletions
diff --git a/apps/projects-web/.version b/apps/projects-web/.version
deleted file mode 100644
index 2eb62bb..0000000
--- a/apps/projects-web/.version
+++ /dev/null
@@ -1 +0,0 @@
-v1-projects
diff --git a/apps/projects-web/.version-dev b/apps/projects-web/.version-dev
deleted file mode 100644
index 1c69081..0000000
--- a/apps/projects-web/.version-dev
+++ /dev/null
@@ -1 +0,0 @@
-v4-projects-dev
diff --git a/apps/projects-web/CHANGELOG.md b/apps/projects-web/CHANGELOG.md
deleted file mode 100644
index 0aa4f12..0000000
--- a/apps/projects-web/CHANGELOG.md
+++ /dev/null
@@ -1,67 +0,0 @@
-# Changelog
-
-## [unreleased]
-
-### Miscellaneous Tasks
-
-- Bump version
-- Update CHANGELOG.md for v4-projects-dev
-
-### Refactor
-
-- Name path basers based on the app it bases
-
-## [unreleased]
-
-### Miscellaneous Tasks
-
-- Bump version
-- Update CHANGELOG.md for v3-projects-dev
-
-## [unreleased]
-
-### Bug Fixes
-
-- .
-- .
-- Incorrect paths
-
-### Miscellaneous Tasks
-
-- Bump version
-- Update CHANGELOG.md for v8-web-app-dev
-- Bump version
-- Update CHANGELOG.md for v7-web-app-dev
-- Bump version
-- Bump version
-- Bump version
-- Bump version
-- Bump version
-- Bump version
-- Update CHANGELOG.md for v14-web-app-dev
-
-## [unreleased]
-
-### Miscellaneous Tasks
-
-- Bump version
-- Update CHANGELOG.md for v13-web-app-dev
-
-## [unreleased]
-
-### Bug Fixes
-
-- Inncorrect paths
-
-### Miscellaneous Tasks
-
-- Bump version
-- Update CHANGELOG.md for v20-web-app
-
-## [unreleased]
-
-### Miscellaneous Tasks
-
-- Bump version
-- Update CHANGELOG.md for v19-web-app
-
diff --git a/apps/projects-web/build_and_push.sh b/apps/projects-web/build_and_push.sh
deleted file mode 100755
index abc8ea9..0000000
--- a/apps/projects-web/build_and_push.sh
+++ /dev/null
@@ -1,76 +0,0 @@
-#!/usr/bin/env bash
-
-set -Eueo pipefail
-
-APP_NAME="projects"
-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/projects.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/projects.greatoffice.life/www
-fi
-
-popd
diff --git a/apps/projects-web/cliff.toml b/apps/projects-web/cliff.toml
deleted file mode 100644
index 955a72b..0000000
--- a/apps/projects-web/cliff.toml
+++ /dev/null
@@ -1,62 +0,0 @@
-# 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/projects-web/src/_assets/pre.css b/apps/projects-web/src/_assets/pre.css
deleted file mode 100644
index 9c9446e..0000000
--- a/apps/projects-web/src/_assets/pre.css
+++ /dev/null
@@ -1,128 +0,0 @@
-: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/projects-web/src/_assets/pwa/android-chrome-192x192.png b/apps/projects-web/src/_assets/pwa/android-chrome-192x192.png
deleted file mode 100644
index 5c098bc..0000000
--- a/apps/projects-web/src/_assets/pwa/android-chrome-192x192.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/android-chrome-512x512.png b/apps/projects-web/src/_assets/pwa/android-chrome-512x512.png
deleted file mode 100644
index 973a1c3..0000000
--- a/apps/projects-web/src/_assets/pwa/android-chrome-512x512.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/apple-touch-icon.png b/apps/projects-web/src/_assets/pwa/apple-touch-icon.png
deleted file mode 100644
index b4d9773..0000000
--- a/apps/projects-web/src/_assets/pwa/apple-touch-icon.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/browserconfig.xml b/apps/projects-web/src/_assets/pwa/browserconfig.xml
deleted file mode 100644
index b3930d0..0000000
--- a/apps/projects-web/src/_assets/pwa/browserconfig.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?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/projects-web/src/_assets/pwa/favicon-16x16.png b/apps/projects-web/src/_assets/pwa/favicon-16x16.png
deleted file mode 100644
index 5dde9f9..0000000
--- a/apps/projects-web/src/_assets/pwa/favicon-16x16.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/favicon-32x32.png b/apps/projects-web/src/_assets/pwa/favicon-32x32.png
deleted file mode 100644
index 9cef4c4..0000000
--- a/apps/projects-web/src/_assets/pwa/favicon-32x32.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/favicon.ico b/apps/projects-web/src/_assets/pwa/favicon.ico
deleted file mode 100644
index 89c7542..0000000
--- a/apps/projects-web/src/_assets/pwa/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/favicon.svg b/apps/projects-web/src/_assets/pwa/favicon.svg
deleted file mode 100644
index 964dbb8..0000000
--- a/apps/projects-web/src/_assets/pwa/favicon.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-<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/projects-web/src/_assets/pwa/manifest.json b/apps/projects-web/src/_assets/pwa/manifest.json
deleted file mode 100644
index 4c550fe..0000000
--- a/apps/projects-web/src/_assets/pwa/manifest.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "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/projects-web/src/_assets/pwa/mstile-144x144.png b/apps/projects-web/src/_assets/pwa/mstile-144x144.png
deleted file mode 100644
index 84d94cb..0000000
--- a/apps/projects-web/src/_assets/pwa/mstile-144x144.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/mstile-150x150.png b/apps/projects-web/src/_assets/pwa/mstile-150x150.png
deleted file mode 100644
index b1398ae..0000000
--- a/apps/projects-web/src/_assets/pwa/mstile-150x150.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/mstile-310x150.png b/apps/projects-web/src/_assets/pwa/mstile-310x150.png
deleted file mode 100644
index 76b16a0..0000000
--- a/apps/projects-web/src/_assets/pwa/mstile-310x150.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/mstile-310x310.png b/apps/projects-web/src/_assets/pwa/mstile-310x310.png
deleted file mode 100644
index d8e4097..0000000
--- a/apps/projects-web/src/_assets/pwa/mstile-310x310.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/mstile-70x70.png b/apps/projects-web/src/_assets/pwa/mstile-70x70.png
deleted file mode 100644
index 0df1e8c..0000000
--- a/apps/projects-web/src/_assets/pwa/mstile-70x70.png
+++ /dev/null
Binary files differ
diff --git a/apps/projects-web/src/_assets/pwa/safari-pinned-tab.svg b/apps/projects-web/src/_assets/pwa/safari-pinned-tab.svg
deleted file mode 100644
index ba2220c..0000000
--- a/apps/projects-web/src/_assets/pwa/safari-pinned-tab.svg
+++ /dev/null
@@ -1,50 +0,0 @@
-<?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/projects-web/src/app/index.d.ts b/apps/projects-web/src/app/index.d.ts
deleted file mode 100644
index c044583..0000000
--- a/apps/projects-web/src/app/index.d.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-/* 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/projects-web/src/app/index.html b/apps/projects-web/src/app/index.html
deleted file mode 100644
index 7e0b0e1..0000000
--- a/apps/projects-web/src/app/index.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!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="./index.ts"></script>
-</body>
-
-</html>
diff --git a/apps/projects-web/src/app/index.scss b/apps/projects-web/src/app/index.scss
deleted file mode 100644
index 4794787..0000000
--- a/apps/projects-web/src/app/index.scss
+++ /dev/null
@@ -1,38 +0,0 @@
-@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/circle-loader';
-@use '../../web-shared/src/styles/components/list';
-@use '../../web-shared/src/styles/components/form-validator';
-@use '../../web-shared/src/styles/components/btn-states';
-@use '../../web-shared/src/styles/components/alert';
-@use '../../web-shared/src/styles/components/details';
-@use '../../web-shared/src/styles/components/tabbed-navigation';
-@use '../../web-shared/src/styles/components/dropdown';
-@use '../../web-shared/src/styles/components/modal';
-@use '../../web-shared/src/styles/components/chip';
-@use '../../web-shared/src/styles/components/autocomplete';
-@use '../../web-shared/src/styles/components/select-autocomplete';
-@use '../../web-shared/src/styles/components/interactive-table';
-@use '../../web-shared/src/styles/components/pagination';
-@use '../../web-shared/src/styles/components/custom-select';
-@use '../../web-shared/src/styles/components/pre-header';
-@use '../../web-shared/src/styles/components/table';
-@use '../../web-shared/src/styles/components/custom-checkbox';
-@use '../../web-shared/src/styles/components/menu';
-@use '../../web-shared/src/styles/components/user-menu';
diff --git a/apps/projects-web/src/app/index.svelte b/apps/projects-web/src/app/index.svelte
deleted file mode 100644
index 9dd2bf8..0000000
--- a/apps/projects-web/src/app/index.svelte
+++ /dev/null
@@ -1,56 +0,0 @@
-<svelte:options immutable={true}/>
-<svelte:window bind:online={online}/>
-
-<script>
- import {logout_user} from "$app/lib/services/user-service";
- import Router from "svelte-spa-router";
- import {wrap} from "svelte-spa-router/wrap";
- import {is_active} from "$shared/lib/session";
- import UiWorkbench from "$app/pages/ui-workbench.svelte";
- import NotFound from "$app/pages/not-found.svelte";
- import Home from "$app/pages/home.svelte";
- import Settings from "$app/pages/settings.svelte";
- import Data from "$app/pages/data.svelte";
- import PreHeader from "$shared/components/pre-header.svelte";
-
- let online = true;
-
- async function user_is_logged_in() {
- if (!await is_active()) {
- await logout_user("expired");
- }
- return true;
- }
-
- const routes = {
- "/home": wrap({
- component: Home,
- conditions: [user_is_logged_in],
- }),
- "/": wrap({
- component: Home,
- conditions: [user_is_logged_in],
- }),
- "/settings": wrap({
- component: Settings,
- conditions: [user_is_logged_in],
- }),
- "/data": wrap({
- component: Data,
- conditions: [user_is_logged_in],
- }),
- "/ui-workbench": UiWorkbench,
- "*": 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/projects-web/src/app/index.ts b/apps/projects-web/src/app/index.ts
deleted file mode 100644
index febb583..0000000
--- a/apps/projects-web/src/app/index.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// @ts-ignore
-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 Debug", "background-color:yellow;color:black;font-size:18px;");
-} else {
- console.log("%c Production; Suppressing logs", "background-color:yellow;color:black;font-size:18px;");
- console.log = noop;
-}
-
-export default new App({
- target: document.getElementById("root"),
-});
diff --git a/apps/projects-web/src/app/lib/services/user-service.ts b/apps/projects-web/src/app/lib/services/user-service.ts
deleted file mode 100644
index 7bffa49..0000000
--- a/apps/projects-web/src/app/lib/services/user-service.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import {delete_account, logout} from "$shared/lib/api/user";
-import {accounts_base} from "$shared/lib/configuration";
-import {clear_session_data} from "$shared/lib/session";
-import {clear_categories} from "$app/lib/stores/categories";
-import {clear_entries} from "$app/lib/stores/entries";
-import {clear_labels} from "$app/lib/stores/labels";
-
-export async function logout_user(reason: string = "") {
- await logout();
- clear_session_data();
- clear_categories();
- clear_labels();
- clear_entries();
- location.replace(accounts_base("#/login" + (reason ? "?" + reason : "")));
-}
-
-export async function delete_user() {
- await delete_account();
- clear_session_data();
- location.replace(accounts_base("#/login?deleted"));
-}
diff --git a/apps/projects-web/src/app/lib/stores/categories.ts b/apps/projects-web/src/app/lib/stores/categories.ts
deleted file mode 100644
index 2a63c42..0000000
--- a/apps/projects-web/src/app/lib/stores/categories.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import {writable, get} from "svelte/store";
-import {create_time_category, delete_time_category, get_time_categories} from "$shared/lib/api/time-entry";
-import type {TimeCategoryDto} from "$shared/lib/models/TimeCategoryDto";
-import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse";
-
-const categories = writable<Array<TimeCategoryDto>>([]);
-
-export async function reload_categories() {
- const get_categories_response = await get_time_categories();
- if (!get_categories_response.ok) {
- clear_categories();
- return;
- }
- categories.set(get_categories_response.data ?? []);
-}
-
-export function clear_categories() {
- categories.set([]);
-}
-
-export async function create_category_async(request: TimeCategoryDto): Promise<IInternalFetchResponse> {
- const create_entry_response = await create_time_category(request);
- if (create_entry_response.ok) {
- const stored_entries = get(categories);
- stored_entries.push(create_entry_response.data);
- categories.set(stored_entries);
- }
- return create_entry_response;
-}
-
-export async function edit_category_async(entry: TimeCategoryDto) {
- if (!entry.id) return;
-}
-
-export async function delete_category_async(entry: TimeCategoryDto) {
- if (!entry.id) return;
- const http_request = await delete_time_category(entry.id);
- if (http_request.ok) {
- const stored_entries = get(categories);
- categories.set(stored_entries.filter(e => e.id !== entry.id));
- }
-}
-
-export default categories;
diff --git a/apps/projects-web/src/app/lib/stores/entries.ts b/apps/projects-web/src/app/lib/stores/entries.ts
deleted file mode 100644
index e933568..0000000
--- a/apps/projects-web/src/app/lib/stores/entries.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import {Temporal} from "@js-temporal/polyfill";
-import {writable, get} from "svelte/store";
-import {get_time_entries, create_time_entry, delete_time_entry, update_time_entry} from "$shared/lib/api/time-entry";
-import type {TimeEntryDto} from "$shared/lib/models/TimeEntryDto";
-import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse";
-import type {TimeEntryQuery} from "$shared/lib/models/TimeEntryQuery";
-
-const entries = writable<Array<TimeEntryDto>>([]);
-
-export function get_time_entry(id: string): TimeEntryDto {
- return get(entries).find(c => c.id === id);
-}
-
-export async function reload_entries(query: TimeEntryQuery): Promise<void> {
- const get_entries_response = await get_time_entries(query);
- if (!get_entries_response.ok) {
- clear_entries();
- return;
- }
- entries.set(get_default_sorted(get_entries_response.data?.results ?? []));
-}
-
-export function clear_entries() {
- entries.set([]);
-}
-
-function get_default_sorted(unsorted: Array<TimeEntryDto>): Array<TimeEntryDto> {
- if (unsorted.length < 1) return unsorted;
- const byStart = unsorted.sort((a, b) => {
- return Temporal.Instant.compare(Temporal.Instant.from(b.start), Temporal.Instant.from(a.start));
- });
-
- return byStart.sort((a, b) => {
- return Temporal.Instant.compare(Temporal.Instant.from(b.stop), Temporal.Instant.from(a.stop));
- });
-}
-
-export async function create_entry_async(request: TimeEntryDto): Promise<IInternalFetchResponse> {
- const create_entry_response = await create_time_entry(request);
- if (create_entry_response.ok) {
- const stored_entries = get(entries) ?? [];
- stored_entries.push(create_entry_response.data);
- entries.set(get_default_sorted(stored_entries));
- }
- return create_entry_response;
-}
-
-export async function edit_entry_async(request: TimeEntryDto): Promise<IInternalFetchResponse> {
- if (!request.id) return;
- const edit_entry_response = await update_time_entry(request);
- if (edit_entry_response.ok) {
- const stored_entries = get(entries) ?? [];
- const index = stored_entries.findIndex(c => c.id === request.id);
- if (index === -1) {
- stored_entries.push(edit_entry_response.data);
- } else {
- stored_entries[index] = edit_entry_response.data;
- }
- entries.set(get_default_sorted(stored_entries));
- }
- return edit_entry_response;
-}
-
-export async function delete_entry_async(entry_id: string): Promise<void> {
- if (!entry_id) throw new Error("No id was supplied when deleting query");
- const delete_entry_response = await delete_time_entry(entry_id);
- if (delete_entry_response.ok) {
- const stored_entries = get(entries) ?? [];
- entries.set(get_default_sorted(stored_entries.filter((e) => e.id !== entry_id) ?? []));
- }
-}
-
-
-export default entries;
diff --git a/apps/projects-web/src/app/lib/stores/labels.ts b/apps/projects-web/src/app/lib/stores/labels.ts
deleted file mode 100644
index d5ffaa9..0000000
--- a/apps/projects-web/src/app/lib/stores/labels.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import {writable, get} from "svelte/store";
-import {create_time_label, delete_time_label, get_time_labels} from "$shared/lib/api/time-entry";
-import type {IInternalFetchResponse} from "$shared/lib/models/IInternalFetchResponse";
-import type {TimeLabelDto} from "$shared/lib/models/TimeLabelDto";
-
-const labels = writable<Array<TimeLabelDto>>([]);
-
-export async function reload_labels() {
- const get_labels_response = await get_time_labels();
- if (!get_labels_response.ok) {
- clear_labels();
- return;
- }
- labels.set(get_labels_response.data ?? []);
-}
-
-export function clear_labels() {
- labels.set([]);
-}
-
-export async function create_label_async(request: TimeLabelDto): Promise<IInternalFetchResponse> {
- const create_label_response = await create_time_label(request);
- if (create_label_response.ok) {
- const stored_entries = get(labels) ?? [];
- stored_entries.push(create_label_response.data);
- labels.set(stored_entries);
- }
- return create_label_response;
-}
-
-export async function edit_label_async(entry: TimeLabelDto) {
- if (!entry.id) throw new Error("Label id is required");
-}
-
-export async function delete_label_async(entry: TimeLabelDto) {
- if (!entry.id) return;
- const http_request = await delete_time_label(entry.id);
- if (http_request.ok) {
- const stored_entries = get(labels) ?? [];
- labels.set(stored_entries.filter(e => e.id !== entry.id));
- }
-}
-
-export default labels;
diff --git a/apps/projects-web/src/app/pages/_layout.svelte b/apps/projects-web/src/app/pages/_layout.svelte
deleted file mode 100644
index 24a9370..0000000
--- a/apps/projects-web/src/app/pages/_layout.svelte
+++ /dev/null
@@ -1,79 +0,0 @@
-<script>
- import {onMount} from "svelte";
- import {location, link} from "svelte-spa-router";
- import {logout_user} from "$app/lib/services/user-service";
- import {random_string, switch_theme} from "$shared/lib/helpers";
- import {get_session_data} from "$shared/lib/session";
- import ProfileModal from "$app/pages/views/profile-modal.svelte";
- import {Menu, MenuItem, MenuItemSeparator} from "$shared/components/menu";
- import Button from "$shared/components/button.svelte";
- import {IconNames} from "$shared/lib/configuration";
-
- let ProfileModalFunctions = {};
- let showUserMenu = false;
- let userMenuTriggerNode;
- const userMenuId = "__menu_" + random_string(3);
- const username = get_session_data().profile.username;
-
- onMount(() => {
- userMenuTriggerNode = document.getElementById("open-user-menu");
- });
-</script>
-
-<ProfileModal bind:functions={ProfileModalFunctions}/>
-
-<nav class="container max-width-xl@md width-fit-content@md width-100% max-width-none margin-y-xs@md margin-bottom-xs block@md position-relative@md position-absolute bottom-unset@md bottom-0">
- <div class="tabs-nav-v2 justify-between">
- <div class="tab-v2">
- <div class="tab-v2">
- <a href="/home"
- use:link
- class="tabs-nav-v2__item {$location === '/home' ? 'tabs-nav-v2__item--selected' : ''}">Home</a>
- </div>
- <div class="tab-v2">
- <a href="/data"
- use:link
- class="tabs-nav-v2__item {$location === '/data' ? 'tabs-nav-v2__item--selected' : ''}">Data</a>
- </div>
- <div class="tab-v2">
- <a href="/settings"
- use:link
- class="tabs-nav-v2__item {$location === '/settings' ? 'tabs-nav-v2__item--selected' : ''}">Settings</a>
- </div>
- </div>
- <div class="tab-v2 padding-x-sm">
- <Button class="user-menu-control"
- variant="reset"
- id="open-user-menu"
- on:click={() => showUserMenu = !showUserMenu}
- text={username}
- icon={IconNames.chevronDown}
- icon_width="2rem"
- icon_height="2rem"
- icon_right_aligned="true"
- title="Toggle user menu"
- aria-controls="{userMenuId}"
- />
- <Menu bind:show="{showUserMenu}"
- trigger={userMenuTriggerNode}
- id="{userMenuId}">
- <div slot="options">
- <MenuItem on:click={() => ProfileModalFunctions.open()}>
- <span title="Administrate your profile">Profile</span>
- </MenuItem>
- <MenuItem on:click={() => switch_theme()}>
- <span title="Change between a dark and light theme">Switch theme</span>
- </MenuItem>
- <MenuItemSeparator/>
- <MenuItem danger="true" on:click={() => logout_user()}>
- <span title="Log out of your profile">Log out</span>
- </MenuItem>
- </div>
- </Menu>
- </div>
- </div>
-</nav>
-
-<main class="container max-width-xl">
- <slot/>
-</main>
diff --git a/apps/projects-web/src/app/pages/data.svelte b/apps/projects-web/src/app/pages/data.svelte
deleted file mode 100644
index 070b98b..0000000
--- a/apps/projects-web/src/app/pages/data.svelte
+++ /dev/null
@@ -1,392 +0,0 @@
-<script>
- import {IconNames} from "$shared/lib/configuration";
- import {onMount} from "svelte";
- import {Temporal} from "@js-temporal/polyfill";
- import Layout from "./_layout.svelte";
- import Modal from "$shared/components/modal.svelte";
- import Tile from "$shared/components/tile.svelte";
- import Icon from "$shared/components/icon.svelte";
- import EntryForm from "$app/pages/views/entry-form/index.svelte";
- import {Table, THead, TBody, TCell, TRow, TablePaginator} from "$shared/components/table";
- import {TimeEntryQueryDuration} from "$shared/lib/models/TimeEntryQuery";
- import {delete_time_entry, get_time_entries, get_time_entry} from "$shared/lib/api/time-entry";
- import {seconds_to_hour_minute_string, is_guid, move_focus, unwrap_date_time_from_entry} from "$shared/lib/helpers";
- import Button from "$shared/components/button.svelte";
-
- let pageCount = 1;
- let page = 1;
-
- const defaultQuery = {
- duration: TimeEntryQueryDuration.THIS_YEAR,
- categories: [],
- labels: [],
- page: page,
- pageSize: 50,
- };
-
- let isLoading;
- let categories = [];
- let labels = [];
- let entries = [];
- let durationSummary = false;
- let EditEntryModal;
- let EditEntryForm;
- let currentTimespanFilter = TimeEntryQueryDuration.THIS_YEAR;
- let currentSpecificDateFilter = Temporal.Now.plainDateTimeISO().subtract({days: 1}).toString().substring(0, 10);
- let currentDateRangeFilter = {};
- let currentCategoryFilter = "all";
- let currentLabelFilter = "all";
- let showDateFilterOptions = false;
- let secondsLogged = 0;
-
- function set_duration_summary_string() {
- if (entries.length > 0) {
- durationSummary = `Showing ${entries.length} ${entries.length === 1 ? "entry" : "entries"}, totalling in ${seconds_to_hour_minute_string(secondsLogged)}`;
- } else {
- durationSummary = "";
- }
- }
-
- async function load_entries(query = defaultQuery) {
- isLoading = true;
- const response = await get_time_entries(query);
- if (response.status === 200) {
- const responseEntries = [];
- secondsLogged = 0;
- for (const entry of response.data.results) {
- const date_time = unwrap_date_time_from_entry(entry);
- const seconds = (date_time.duration.hours * 60 * 60) + (date_time.duration.minutes * 60);
- responseEntries.push({
- id: entry.id,
- date: date_time.start_date,
- start: date_time.start_time,
- stop: date_time.stop_time,
- durationString: date_time.duration.hours + "h" + date_time.duration.minutes + "m",
- seconds: seconds,
- category: entry.category,
- labels: entry.labels,
- description: entry.description,
- });
- secondsLogged += seconds;
- }
- entries = responseEntries;
- page = response.data.page;
- pageCount = response.data.totalPageCount;
- } else {
- entries = [];
- page = 0;
- pageCount = 0;
- }
- isLoading = false;
- set_duration_summary_string();
- }
-
- function load_entries_with_filter(page = 1) {
- let query = defaultQuery;
- query.duration = currentTimespanFilter;
- query.labels = [];
- query.categories = [];
- query.page = page;
-
- if (currentTimespanFilter === TimeEntryQueryDuration.SPECIFIC_DATE) {
- query.specificDate = currentSpecificDateFilter;
- } else {
- delete query.specificDate;
- }
-
- if (currentTimespanFilter === TimeEntryQueryDuration.DATE_RANGE) {
- query.dateRange = currentDateRangeFilter;
- } else {
- delete query.dateRange;
- }
-
- if ((currentCategoryFilter !== "all" && currentCategoryFilter?.length > 0) ?? false) {
- for (const chosenCategoryId of currentCategoryFilter) {
- if (chosenCategoryId === "all") {
- continue;
- }
- query.categories.push({
- id: chosenCategoryId,
- });
- }
- }
-
- if ((currentLabelFilter !== "all" && currentLabelFilter?.length > 0) ?? false) {
- for (const chosenLabelId of currentLabelFilter) {
- if (chosenLabelId === "all") {
- continue;
- }
- query.labels.push({
- id: chosenLabelId,
- });
- }
- }
-
- load_entries(query);
- }
-
- async function handle_delete_entry_button_click(e, entryId) {
- if (confirm("Are you sure you want to delete this entry?")) {
- const response = await delete_time_entry(entryId);
- if (response.ok) {
- const indexOfEntry = entries.findIndex((c) => c.id === entryId);
- if (indexOfEntry !== -1) {
- secondsLogged -= entries[indexOfEntry].seconds;
- entries.splice(indexOfEntry, 1);
- entries = entries;
- set_duration_summary_string();
- }
- }
- }
- }
-
- function handle_edit_entry_form_updated() {
- load_entries_with_filter(page);
- EditEntryModal.close();
- }
-
- async function handle_edit_entry_button_click(event, entryId) {
- const response = await get_time_entry(entryId);
- if (response.status === 200) {
- if (is_guid(response.data.id)) {
- EditEntryForm.set_values(response.data);
- EditEntryModal.open();
- move_focus(document.querySelector("input[id='date']"));
- }
- }
- }
-
- function close_date_filter_box(event) {
- if (!event.target.closest(".date_filter_box_el")) {
- showDateFilterOptions = false;
- window.removeEventListener("click", close_date_filter_box);
- }
- }
-
- function toggle_date_filter_box(event) {
- const box = document.getElementById("date_filter_box");
- const rect = event.target.getBoundingClientRect();
- box.style.top = rect.y + "px";
- box.style.left = rect.x - 50 + "px";
- showDateFilterOptions = true;
- window.addEventListener("click", close_date_filter_box);
- }
-
- onMount(() => {
- isLoading = true;
- Promise.all([load_entries()]).then(() => {
- isLoading = false;
- });
- });
-</script>
-
-<Modal title="Edit entry"
- bind:functions={EditEntryModal}
- on:closed={() => EditEntryForm.reset()}>
- <EntryForm bind:functions={EditEntryForm}
- on:updated={handle_edit_entry_form_updated}/>
-</Modal>
-
-<div id="date_filter_box"
- style="margin-top:25px"
- class="padding-xs z-index-overlay bg shadow-sm position-absolute date_filter_box_el border {showDateFilterOptions ? '' : 'hide'}">
- <div class="flex items-baseline margin-bottom-xxxxs">
- <label class="text-sm color-contrast-medium margin-right-xs"
- for="durationSelect">Timespan:</label>
- <div class="select inline-block js-select">
- <select name="durationSelect"
- bind:value={currentTimespanFilter}
- id="durationSelect">
- <option value={TimeEntryQueryDuration.TODAY}
- selected> Today
- </option>
- <option value={TimeEntryQueryDuration.THIS_WEEK}>This week</option>
- <option value={TimeEntryQueryDuration.THIS_MONTH}>This month</option>
- <option value={TimeEntryQueryDuration.THIS_YEAR}>This year</option>
- <option value={TimeEntryQueryDuration.SPECIFIC_DATE}>Spesific date</option>
- <option value={TimeEntryQueryDuration.DATE_RANGE}>Date range</option>
- </select>
-
- <svg class="icon icon--xxxs margin-left-xxs"
- viewBox="0 0 8 8">
- <path d="M7.934,1.251A.5.5,0,0,0,7.5,1H.5a.5.5,0,0,0-.432.752l3.5,6a.5.5,0,0,0,.864,0l3.5-6A.5.5,0,0,0,7.934,1.251Z"/>
- </svg>
- </div>
- </div>
-
- {#if currentTimespanFilter === TimeEntryQueryDuration.SPECIFIC_DATE}
- <div class="flex items-baseline margin-bottom-xxxxs justify-between">
- <span class="text-sm color-contrast-medium margin-right-xs">Date:</span>
- <span class="text-sm">
- <input type="date"
- class="border-none padding-0 color-inherit bg-transparent"
- bind:value={currentSpecificDateFilter}/>
- </span>
- </div>
- {/if}
-
- {#if currentTimespanFilter === TimeEntryQueryDuration.DATE_RANGE}
- <div class="flex items-baseline margin-bottom-xxxxs justify-between">
- <span class="text-sm color-contrast-medium margin-right-xs">From:</span>
- <span class="text-sm">
- <input type="date"
- class="border-none padding-0 color-inherit bg-transparent"
- on:change={(e) => (currentDateRangeFilter.from = e.target.value)}/>
- </span>
- </div>
-
- <div class="flex items-baseline margin-bottom-xxxxs justify-between">
- <span class="text-sm color-contrast-medium margin-right-xs">To:</span>
- <span class="text-sm">
- <input type="date"
- class="border-none padding-0 color-inherit bg-transparent"
- on:change={(e) => (currentDateRangeFilter.to = e.target.value)}/>
- </span>
- </div>
- {/if}
-
- <div class="flex items-baseline justify-end">
- <Button variant="subtle"
- on:click={() => load_entries_with_filter(page)}
- class="text-sm"
- text="Save"/>
- </div>
-</div>
-
-<Layout>
- <Tile class="{isLoading ? 'c-disabled loading' : ''}">
- <nav class="s-tabs text-sm">
- <ul class="s-tabs__list">
- <li><span class="s-tabs__link s-tabs__link--current">All (21)</span></li>
- <li><span class="s-tabs__link">Published (19)</span></li>
- <li><span class="s-tabs__link">Draft (2)</span></li>
- </ul>
- </nav>
- <div class="max-width-100% overflow-auto"
- style="max-height: 82.5vh">
- <Table class="text-sm width-100% int-table--sticky-header">
- <THead>
- <TCell type="th"
- style="width: 30px;">
- <div class="custom-checkbox int-table__checkbox">
- <input class="custom-checkbox__input"
- type="checkbox"
- aria-label="Select all rows"/>
- <div class="custom-checkbox__control"
- aria-hidden="true"></div>
- </div>
- </TCell>
-
- <TCell type="th"
- style="width: 100px">
- <div class="flex items-center justify-between">
- <span>Date</span>
- <div class="date_filter_box_el cursor-pointer"
- on:click={toggle_date_filter_box}>
- <Icon name="{IconNames.funnel}"/>
- </div>
- </div>
- </TCell>
-
- <TCell type="th"
- style="width: 100px">
- <div class="flex items-center">
- <span>Duration</span>
- </div>
- </TCell>
-
- <TCell type="th"
- style="width: 100px;">
- <div class="flex items-center">
- <span>Category</span>
- </div>
- </TCell>
-
- <TCell type="th"
- style="width: 300px;">
- <div class="flex items-center">
- <span>Description</span>
- </div>
- </TCell>
- <TCell type="th"
- style="width: 50px"></TCell>
- </THead>
- <TBody>
- {#if entries.length > 0}
- {#each entries as entry}
- <TRow class="text-nowrap"
- data-id={entry.id}>
- <TCell type="th"
- thScope="row">
- <div class="custom-checkbox int-table__checkbox">
- <input class="custom-checkbox__input"
- type="checkbox"
- aria-label="Select this row"/>
- <div class="custom-checkbox__control"
- aria-hidden="true"></div>
- </div>
- </TCell>
- <TCell>
- <pre>{entry.date.toLocaleString()}</pre>
- </TCell>
- <TCell>
- <pre class="flex justify-between">
- <div class="flex justify-between">
- <span>{entry.start.toLocaleString(undefined, {timeStyle: "short"})}</span>
- <span> - </span>
- <span>{entry.stop.toLocaleString(undefined, {timeStyle: "short"})}</span>
- </div>
- </pre>
- </TCell>
- <TCell>
- <span data-id={entry.category.id}>{entry.category.name}</span>
- </TCell>
- <TCell class="text-truncate max-width-xxxxs"
- title="{entry.description}">
- {entry.description ?? ""}
- </TCell>
- <TCell class="flex flex-row justify-end items-center">
- <Button icon="{IconNames.pencilSquare}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={(e) => handle_edit_entry_button_click(e, entry.id)}
- title="Edit entry"/>
- <Button icon="{IconNames.trash}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={(e) => handle_delete_entry_button_click(e, entry.id)}
- title="Delete entry"/>
- </TCell>
- </TRow>
- {/each}
- {:else}
- <TRow class="text-nowrap">
- <TCell type="th"
- thScope="row"
- colspan="7">
- {isLoading ? "Loading..." : "No entries"}
- </TCell>
- </TRow>
- {/if}
- </TBody>
- </Table>
- </div>
- <div class="flex items-center justify-between">
- <p class="text-sm">
- {#if durationSummary}
- <small class={isLoading ? "c-disabled loading" : ""}>{durationSummary}</small>
- {:else}
- <small class={isLoading ? "c-disabled loading" : ""}>No entries</small>
- {/if}
- </p>
-
- <nav class="grid padding-y-sm {isLoading ? 'c-disabled loading' : ''}">
- <TablePaginator {page}
- on:value_change={(e) => load_entries_with_filter(e.detail.newValue)}
- {pageCount}/>
- </nav>
- </div>
- </Tile>
-</Layout>
diff --git a/apps/projects-web/src/app/pages/home.svelte b/apps/projects-web/src/app/pages/home.svelte
deleted file mode 100644
index c3e7af4..0000000
--- a/apps/projects-web/src/app/pages/home.svelte
+++ /dev/null
@@ -1,167 +0,0 @@
-<script lang="ts">
- import {IconNames} from "$shared/lib/configuration";
- import {TimeEntryDto} from "$shared/lib/models/TimeEntryDto";
- import {Temporal} from "@js-temporal/polyfill";
- import {onMount} from "svelte";
- import Tile from "$shared/components/tile.svelte";
- import Button from "$shared/components/button.svelte";
- import Stopwatch from "$shared/components/stopwatch.svelte";
- import {Table, THead, TBody, TCell, TRow} from "$shared/components/table";
- import Layout from "./_layout.svelte";
- import EntryFrom from "$app/pages/views/entry-form/index.svelte";
- import {seconds_to_hour_minute_string, unwrap_date_time_from_entry} from "$shared/lib/helpers";
- import {TimeEntryQueryDuration} from "$shared/lib/models/TimeEntryQuery";
- import entries, {delete_entry_async, get_time_entry, reload_entries} from "$app/lib/stores/entries";
-
- let currentTime = "";
- let isLoading = false;
- let EditEntryForm: any;
- let timeEntries = [] as Array<TimeEntryDto>;
- let timeLoggedTodayString = "0h0m";
-
- function set_current_time() {
- currentTime = Temporal.Now.plainTimeISO().toLocaleString(undefined, {
- timeStyle: "short",
- });
- }
-
- async function on_edit_entry_button_click(event, entryId: string) {
- const response = get_time_entry(entryId);
- EditEntryForm.set_values(response);
- }
-
- async function on_delete_entry_button_click(event, entryId: string) {
- if (confirm("Are you sure you want to delete this entry?")) {
- await delete_entry_async(entryId);
- }
- }
-
- async function load_todays_entries() {
- await reload_entries({
- duration: TimeEntryQueryDuration.TODAY,
- page: 1,
- pageSize: 100,
- });
- }
-
- function on_create_from_stopwatch(event) {
- EditEntryForm.set_time({to: event.detail.to, from: event.detail.from});
- if (event.detail.description) {
- EditEntryForm.set_description(event.detail.description);
- }
- }
-
- onMount(async () => {
- set_current_time();
- setInterval(() => {
- set_current_time();
- }, 1e4);
- await load_todays_entries();
- entries.subscribe((val) => {
- const newEntries = [];
- let loggedSecondsToday = 0;
- for (const entry of val) {
- const date_time = unwrap_date_time_from_entry(entry);
- newEntries.push({
- id: entry.id,
- start: date_time.start_time,
- stop: date_time.stop_time,
- category: entry.category,
- });
- loggedSecondsToday += (date_time.duration.hours * 60 * 60) + (date_time.duration.minutes * 60);
- }
- timeLoggedTodayString = seconds_to_hour_minute_string(loggedSecondsToday);
- timeEntries = newEntries;
- });
- });
-</script>
-
-<Layout>
- <div class="grid gap-md margin-top-xs flex-row@md items-start flex-column-reverse">
- <Tile class="col">
- <h3 class="text-md padding-bottom-xxxs">New entry</h3>
- <EntryFrom bind:functions={EditEntryForm}/>
- </Tile>
- <div class="col grid gap-sm">
- <Tile class="col-6@md col-12">
- <p class="text-xxl">{timeLoggedTodayString}</p>
- <p class="text-xs margin-bottom-xxs">Logged time today</p>
- <pre class="text-xxl">{currentTime}</pre>
- <p class="text-xs">Current time</p>
- </Tile>
- <Tile class="col-6@md col-12">
- <Stopwatch on:create={on_create_from_stopwatch}>
- <h3 slot="header"
- class="text-md">Stopwatch</h3>
- </Stopwatch>
- </Tile>
- <Tile class="col-12">
- <h3 class="text-md padding-bottom-xxxs">Today's entries</h3>
- <div class="max-width-100% overflow-auto">
- <Table class="width-100% text-sm">
- <THead>
- <TCell type="th"
- class="text-left">
- <span>Category</span>
- </TCell>
- <TCell type="th"
- class="text-left">
- <span>Timespan</span>
- </TCell>
- <TCell type="th"
- class="text-right">
- <Button icon="{IconNames.refresh}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- title="Refresh today's entries"
- on:click={load_todays_entries}/>
- </TCell>
- </THead>
- <TBody>
- {#if timeEntries.length > 0}
- {#each timeEntries as entry}
- <TRow class="text-nowrap text-left"
- data-id={entry.id}>
- <TCell>
- <span data-id={entry.category?.id}>
- {entry.category?.name}
- </span>
- </TCell>
- <TCell>
- {entry.start.toLocaleString(undefined, {timeStyle: "short"})}
- <span>-</span>
- {entry.stop.toLocaleString(undefined, {timeStyle: "short"})}
- </TCell>
- <TCell class="flex flex-row justify-end items-center">
- <Button icon="{IconNames.pencilSquare}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={(e) => on_edit_entry_button_click(e, entry.id)}
- title="Edit entry"/>
- <Button icon="{IconNames.trash}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={(e) => on_delete_entry_button_click(e, entry.id)}
- title="Delete entry"/>
- </TCell>
- </TRow>
- {/each}
- {:else}
- <TRow class="text-nowrap">
- <TCell type="th"
- thScope="row"
- colspan="7">
- {isLoading ? "Loading..." : "No entries today"}
- </TCell>
- </TRow>
- {/if}
- </TBody>
- </Table>
- </div>
- </Tile>
- </div>
- </div>
-</Layout>
diff --git a/apps/projects-web/src/app/pages/not-found.svelte b/apps/projects-web/src/app/pages/not-found.svelte
deleted file mode 100644
index 46d0d1d..0000000
--- a/apps/projects-web/src/app/pages/not-found.svelte
+++ /dev/null
@@ -1,24 +0,0 @@
-<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/projects-web/src/app/pages/settings.svelte b/apps/projects-web/src/app/pages/settings.svelte
deleted file mode 100644
index ca9fd47..0000000
--- a/apps/projects-web/src/app/pages/settings.svelte
+++ /dev/null
@@ -1,12 +0,0 @@
-<script>
- import Layout from "./_layout.svelte";
- import CategoriesTile from "$app/pages/views/settings-categories-tile.svelte";
- import LabelsTile from "$app/pages/views/settings-labels-tile.svelte";
-</script>
-
-<Layout>
- <section class="grid gap-md">
- <CategoriesTile/>
- <LabelsTile/>
- </section>
-</Layout>
diff --git a/apps/projects-web/src/app/pages/ui-workbench.svelte b/apps/projects-web/src/app/pages/ui-workbench.svelte
deleted file mode 100644
index 5e92c9d..0000000
--- a/apps/projects-web/src/app/pages/ui-workbench.svelte
+++ /dev/null
@@ -1,48 +0,0 @@
-<script>
- import Dropdown from "$shared/components/dropdown.svelte";
- import {generate_random_hex_color} from "$shared/lib/colors";
- import {uuid_v4} from "$shared/lib/helpers";
-
- let entries = [];
-
- let dropdown;
-
- for (let i = 1; i < 20; i++) {
- entries.push({
- id: uuid_v4(),
- name: "Option " + i,
- selected: false,
- color: generate_random_hex_color(true)
- });
- }
-
- function on_create({detail}) {
- const copy = entries;
- const entry = {id: uuid_v4(), name: detail.name};
- copy.push(entry);
- entries = copy;
- console.log("Created", entry);
- dropdown.select_entry(entry.id);
- }
-
- function on_select({detail}) {
- console.log(detail);
- }
-</script>
-
-<main class="grid gap-y-lg padding-md">
- <div class="row">
- <label for="dropdown">Choose an entry</label>
- <Dropdown id="dropdown"
- name="dropdown"
- placeholder="Search or create"
- maxlength="50"
- creatable="true"
- multiple="false"
- {entries}
- bind:this={dropdown}
- on:create={on_create}
- on:select={on_select}
- />
- </div>
-</main>
diff --git a/apps/projects-web/src/app/pages/views/category-form/index.svelte b/apps/projects-web/src/app/pages/views/category-form/index.svelte
deleted file mode 100644
index e8c0f94..0000000
--- a/apps/projects-web/src/app/pages/views/category-form/index.svelte
+++ /dev/null
@@ -1,144 +0,0 @@
-<script lang="ts">
- import Alert from "$shared/components/alert.svelte";
- import Dropdown from "$shared/components/dropdown.svelte";
- import labels, {reload_labels, create_label_async} from "$app/lib/stores/labels";
- import {generate_random_hex_color} from "$shared/lib/colors";
- import {get} from "svelte/store";
-
- let LabelsDropdown;
-
- const dough = {
- error: "",
- fields: {
- name: {
- value: "",
- error: "",
- validate() {
- return false;
- }
- },
- color: {
- value: "",
- error: "",
- validate() {
- return true;
- }
- },
- labels: {
- loading: false,
- value: [],
- error: "",
- validate() {
- return true;
- },
- async create({name}) {
- dough.fields.labels.loading = true;
- const response = await create_label_async({
- name: name,
- color: generate_random_hex_color(),
- });
- dough.fields.labels.loading = false;
- if (response.ok) {
- // Small pause to allow loading state to update everywhere.
- setTimeout(() => LabelsDropdown.select_entry(response.data.id), 50);
- }
- }
- },
- archived: {
- value: false,
- error: "",
- validate() {
- return true;
- }
- }
- },
- bake() {
- // labels.filter((c) => Object.hasOwn(c, "selected") && c.selected === true);
- return {
- labels: dough.fields.labels.value,
- name: dough.fields.name.value,
- color: dough.fields.color.value,
- };
- },
- submit(event) {
- const bread = dough.bake();
- console.log(bread);
- console.log("Submitted");
- }
- };
-
- const functions = {
- set(values) {
- functions.set_archived(values.archived);
- functions.set_labels(values.labels);
- functions.set_color(values.color);
- functions.set_name(values.name);
- },
- is_valid() {
- let isValid = true;
- if (!dough.fields.labels.validate()) isValid = false;
- if (!dough.fields.color.validate()) isValid = false;
- if (!dough.fields.name.validate()) isValid = false;
- if (!dough.fields.archived.validate()) isValid = false;
- return isValid;
- },
- set_archived(value) {
- dough.fields.archived.value = value;
- },
- set_labels(value) {
- dough.fields.labels.value = value;
- },
- set_color(value) {
- dough.fields.color.value = value;
- },
- set_name(value) {
- dough.fields.name.value = value;
- },
- };
-</script>
-
-<form on:submit|preventDefault={dough.submit}>
- <div class="margin-y-sm">
- <Alert visible={dough.error !== ""}
- message={dough.error}
- type="error"/>
- </div>
- <div class="grid gap-x-xs margin-bottom-sm">
- <div class="col-10">
- <label for="name"
- class="form-label margin-bottom-xxs">Name</label>
- <input type="text"
- class="form-control width-100%"
- id="name"
- bind:value={dough.fields.name.value}/>
- {#if dough.fields.name.error}
- <small class="color-error">{dough.fields.name.error}</small>
- {/if}
- </div>
- <div class="col-2">
- <label for="color"
- class="form-label margin-bottom-xxs">Color</label>
- <input type="color"
- class="form-control width-100%"
- id="color"
- style="height: 41px"
- bind:value={dough.fields.color.value}/>
- {#if dough.fields.color.error}
- <small class="color-error">{dough.fields.color.error}</small>
- {/if}
- </div>
- </div>
- <div class="margin-bottom-sm">
- <label for="labels"
- class="form-label margin-bottom-xxs">Default labels</label>
- <Dropdown id="labels"
- createable={true}
- placeholder="Search or create"
- entries={$labels}
- multiple={true}
- on_create_async={(name) => dough.fields.labels.create({name})}/>
- {#if dough.fields.labels.error}
- <small class="color-error">{dough.fields.labels.error}</small>
- {/if}
- </div>
-</form>
diff --git a/apps/projects-web/src/app/pages/views/data-table-paginator.svelte b/apps/projects-web/src/app/pages/views/data-table-paginator.svelte
deleted file mode 100644
index 7696ca2..0000000
--- a/apps/projects-web/src/app/pages/views/data-table-paginator.svelte
+++ /dev/null
@@ -1,107 +0,0 @@
-<script>
- import {createEventDispatcher, onMount} from "svelte";
- import {restrict_input_to_numbers} from "$shared/lib/helpers";
-
- const dispatch = createEventDispatcher();
- export let page = 1;
- export let pageCount = 1;
- let prevCount = page;
- let canIncrement = false;
- let canDecrement = false;
- $: canIncrement = page < pageCount;
- $: canDecrement = page > 1;
-
- onMount(() => {
- restrict_input_to_numbers(document.querySelector("#curr-page"));
- });
-
- function increment() {
- if (canIncrement) {
- page++;
- }
- }
-
- function decrement() {
- if (canDecrement) {
- page--;
- }
- }
-
- $: if (page) {
- handle_change();
- }
-
- function handle_change() {
- if (page === prevCount) {
- return;
- }
- prevCount = page;
- if (page > pageCount) {
- page = pageCount;
- }
- dispatch("value_change", {
- newValue: page,
- });
- }
-</script>
-
-<nav class="pagination"
- aria-label="Pagination">
- <ul
- class="pagination__list flex flex-wrap gap-xxxs justify-center justify-end@md"
- >
- <li>
- <button
- on:click={decrement}
- class="reset pagination__item {canDecrement ? '' : 'c-disabled'}"
- >
- <svg class="icon icon--xs flip-x"
- viewBox="0 0 16 16"
- ><title>Go to previous page</title>
- <polyline
- points="6 2 12 8 6 14"
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- />
- </svg>
- </button>
- </li>
-
- <li>
- <span class="pagination__jumper flex items-center">
- <input
- aria-label="Page number"
- class="form-control"
- id="curr-page"
- type="text"
- on:change={handle_change}
- value={page}
- />
- <em>of {pageCount}</em>
- </span>
- </li>
-
- <li>
- <button
- on:click={increment}
- class="reset pagination__item {canIncrement ? '' : 'c-disabled'}"
- >
- <svg class="icon icon--xs"
- viewBox="0 0 16 16"
- ><title>Go to next page</title>
- <polyline
- points="6 2 12 8 6 14"
- fill="none"
- stroke="currentColor"
- stroke-linecap="round"
- stroke-linejoin="round"
- stroke-width="2"
- />
- </svg>
- </button>
- </li>
- </ul>
-</nav>
diff --git a/apps/projects-web/src/app/pages/views/entry-form/index.svelte b/apps/projects-web/src/app/pages/views/entry-form/index.svelte
deleted file mode 100644
index cb974ed..0000000
--- a/apps/projects-web/src/app/pages/views/entry-form/index.svelte
+++ /dev/null
@@ -1,196 +0,0 @@
-<script lang="ts">
- import {TimeEntryDto} from "$shared/lib/models/TimeEntryDto";
- import {Temporal} from "@js-temporal/polyfill";
- import {createEventDispatcher, onMount, onDestroy} from "svelte";
- import DateTimePart from "./sections/date-time.svelte";
- import LabelsPart from "./sections/labels.svelte";
- import CategoryPart from "./sections/category.svelte";
- import Button from "$shared/components/button.svelte";
- import {Textarea} from "$shared/components/form";
- import Alert from "$shared/components/alert.svelte";
- import {is_guid} from "$shared/lib/helpers";
- import {create_entry_async, edit_entry_async} from "$app/lib/stores/entries";
-
- const dispatch = createEventDispatcher();
-
- let formError = "";
- let formIsLoading = false;
- let description = "";
- let descriptionError = "";
- let dateTimePart;
- let labelsPart;
- let categoryPart;
- let entryId;
-
- onMount(() => {
- formIsLoading = true;
-
- Promise.all([categoryPart.load_categories(), labelsPart.load_labels()]).then(() => {
- formIsLoading = false;
- });
-
- window.addEventListener("keydown", handle_window_keydown);
- });
-
- onDestroy(() => {
- window.removeEventListener("keydown", handle_window_keydown);
- });
-
- function handle_window_keydown(event) {
- if (event.ctrlKey && event.code === "Enter") {
- submit_form();
- }
- }
-
- function validate_form() {
- return dateTimePart.is_valid() && categoryPart.is_valid() && description_is_valid();
- }
-
- function description_is_valid() {
- if (!description) {
- descriptionError = "Description is required";
- } else {
- descriptionError = "";
- }
-
- return description;
- }
-
- function get_payload() {
- const response = {} as TimeEntryDto;
- const values = get_values();
- if (!is_guid(values.id)) {
- delete values.id;
- } else {
- response.id = values.id;
- }
-
- const currentTimeZone = Temporal.Now.zonedDateTimeISO().offset;
- response.start = values.date + "T" + values.fromTimeValue + currentTimeZone.toString();
- response.stop = values.date + "T" + values.toTimeValue + currentTimeZone.toString();
-
- response.category = {
- id: values.category.id,
- };
-
- const selectedLabels = values.labels;
- if (selectedLabels?.length > 0 ?? false) {
- response.labels = selectedLabels;
- }
-
- const descriptionContent = description?.trim();
- if (descriptionContent?.length > 0 ?? false) {
- response.description = descriptionContent;
- }
-
- return response;
- }
-
- async function submit_form() {
- formError = "";
- if (validate_form()) {
- const payload = get_payload() as TimeEntryDto;
- formIsLoading = true;
- if (is_guid(payload.id)) {
- const response = await edit_entry_async(payload);
- if (response.ok) {
- functions.reset();
- dispatch("updated", response.data);
- } else {
- formError = "An error occured while updating the entry, try again soon";
- formIsLoading = false;
- }
- } else {
- const response = await create_entry_async(payload);
- if (response.ok) {
- functions.reset();
- dispatch("created");
- } else {
- formError = "An error occured while creating the entry, try again soon";
- formIsLoading = false;
- }
- }
- }
- }
-
- function get_values() {
- return {
- id: entryId,
- toTimeValue: dateTimePart.get_to_time_value(),
- fromTimeValue: dateTimePart.get_from_time_value(),
- date: dateTimePart.get_date(),
- category: categoryPart.get_selected(),
- labels: labelsPart.get_selected(),
- description: description,
- };
- }
-
- export const functions = {
- set_values(values) {
- entryId = values.id;
- dateTimePart.set_values(values);
- labelsPart.select_labels(values?.labels.map((c) => c.id) ?? []);
- categoryPart.select_category(values?.category?.id);
- description = values.description;
- },
- set_time(value: {to: Temporal.PlainTime, from: Temporal.PlainTime}) {
- dateTimePart.set_times(value);
- },
- set_description(value: string) {
- if (description) description = description + "\n\n" + value;
- else description = value;
- },
- reset() {
- formIsLoading = false;
- entryId = "";
- labelsPart.reset();
- categoryPart.reset();
- dateTimePart.reset(true);
- description = "";
- formError = "";
- },
- };
-</script>
-
-<form on:submit|preventDefault={submit_form}
- on:reset={() => functions.reset()}>
- <div class="margin-y-sm">
- <Alert visible={formError !== ""}
- message={formError}
- type="error"/>
- </div>
-
- <div class="margin-bottom-sm">
- <DateTimePart bind:functions={dateTimePart}/>
- </div>
-
- <div class="margin-bottom-sm">
- <CategoryPart bind:functions={categoryPart}/>
- </div>
-
- <div class="margin-bottom-sm">
- <LabelsPart bind:functions={labelsPart}/>
- </div>
-
- <div class="margin-bottom-sm">
- <Textarea class="width-100%"
- id="description"
- label="Description"
- errorText="{descriptionError}"
- bind:value={description}></Textarea>
- </div>
-
- <div class="flex flex-row justify-end gap-x-xs">
- {#if entryId}
- <Button text="Reset"
- on:click={() => functions.reset()}
- variant="subtle"
- />
- {/if}
- <Button loading={formIsLoading}
- type="submit"
- variant="primary"
- text={entryId ? "Save" : "Create"}
- />
- </div>
-</form>
diff --git a/apps/projects-web/src/app/pages/views/entry-form/sections/category.svelte b/apps/projects-web/src/app/pages/views/entry-form/sections/category.svelte
deleted file mode 100644
index f98c045..0000000
--- a/apps/projects-web/src/app/pages/views/entry-form/sections/category.svelte
+++ /dev/null
@@ -1,75 +0,0 @@
-<script>
- import {generate_random_hex_color} from "$shared/lib/colors";
- import Dropdown from "$shared/components/dropdown.svelte";
- import {is_guid, move_focus} from "$shared/lib/helpers";
- import categories, {reload_categories, create_category_async} from "$app/lib/stores/categories";
-
- let categoriesError = "";
- let loading = false;
-
- let DropdownExports;
-
- function reset() {
- DropdownExports.reset();
- categoriesError = "";
- console.log("Reset category-part");
- }
-
- async function on_create({name}) {
- loading = true;
- const response = await create_category_async({
- name: name,
- color: generate_random_hex_color(),
- });
- loading = false;
- if (response.ok) {
- // Small pause to allow loading state to update everywhere.
- setTimeout(() => select_category(response.data.id), 50);
- }
- }
-
- function get_selected() {
- return $categories.find((c) => c.selected === true);
- }
-
- function select_category(id) {
- DropdownExports.select(id);
- }
-
- function is_valid() {
- let isValid = true;
- const category = get_selected();
- if (!is_guid(category?.id)) {
- categoriesError = "Category is required";
- isValid = false;
- move_focus(document.getElementById("category-dropdown"));
- } else {
- categoriesError = "";
- }
- return isValid;
- }
-
- export const functions = {
- get_selected,
- reset,
- is_valid,
- select_category,
- load_categories: reload_categories,
- };
-</script>
-
-<Dropdown
- entries={$categories}
- label="Category"
- maxlength="50"
- createable={true}
- placeholder="Search or create"
- id="category-dropdown"
- loading={loading}
- name="category-dropdown"
- on_create_async={on_create}
- noResultsText="No categories available (Create a new one by searching for it and pressing enter)"
- errorText="{categoriesError}"
- bind:this={DropdownExports}
-/>
-
diff --git a/apps/projects-web/src/app/pages/views/entry-form/sections/date-time.svelte b/apps/projects-web/src/app/pages/views/entry-form/sections/date-time.svelte
deleted file mode 100644
index c91e014..0000000
--- a/apps/projects-web/src/app/pages/views/entry-form/sections/date-time.svelte
+++ /dev/null
@@ -1,165 +0,0 @@
-<script lang="ts">
- import {Temporal} from "@js-temporal/polyfill";
-
- // TIME
- let fromTimeValue = "";
- let fromTimeError = "";
- let toTimeValue = "";
- let toTimeError = "";
-
- function handle_from_time_changed(e) {
- fromTimeValue = e.target.value;
- if (fromTimeValue) {
- fromTimeError = "";
- }
- }
-
- function handle_to_time_changed(e) {
- toTimeValue = e.target.value;
- if (toTimeValue) {
- toTimeError = "";
- }
- }
-
- // DATE
- let date = Temporal.Now.plainDateTimeISO().toString().substring(0, 10);
- let dateError = "";
-
- function is_valid() {
- let isValid = true;
- let focusIsSet = false;
- if (!date) {
- dateError = "Date is required";
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("date")?.focus();
- focusIsSet = true;
- }
- } else {
- dateError = "";
- }
-
- if (!fromTimeValue) {
- fromTimeError = "From is required";
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("from")?.focus();
- focusIsSet = true;
- }
- } else if (toTimeValue && fromTimeValue > toTimeValue) {
- fromTimeError = "From can not be after To";
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("from")?.focus();
- focusIsSet = true;
- }
- } else if (fromTimeValue === toTimeValue) {
- fromTimeError = "From and To can not be equal";
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("from")?.focus();
- focusIsSet = true;
- }
- } else {
- fromTimeError = "";
- }
-
- if (!toTimeValue) {
- toTimeError = "To is required";
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("to")?.focus();
- focusIsSet = true;
- }
- } else if (fromTimeValue && toTimeValue < fromTimeValue) {
- toTimeError = "To can not be before From";
- isValid = false;
- if (!focusIsSet) {
- document.getElementById("to")?.focus();
- focusIsSet = true;
- }
- } else {
- toTimeError = "";
- }
-
- return isValid;
- }
-
- export const functions = {
- get_from_time_value() {
- return fromTimeValue;
- },
- get_to_time_value() {
- return toTimeValue;
- },
- get_date() {
- return date;
- },
- is_valid,
- reset(focusDate = false) {
- fromTimeValue = "";
- toTimeValue = "";
- if (focusDate) {
- document.getElementById("date")?.focus();
- }
- },
- set_times(value) {
- console.log(value);
- fromTimeValue = value.from.toString().substring(0, 5);
- toTimeValue = value.to.toString().substring(0, 5);
- },
- set_date(new_date: Temporal.PlainDate) {
- date = new_date.toString();
- },
- set_values(values) {
- const currentTimeZone = Temporal.Now.timeZone().id;
- const startDate = Temporal.Instant.from(values.start);
- const stopDate = Temporal.Instant.from(values.stop);
- fromTimeValue = startDate.toZonedDateTimeISO(currentTimeZone).toPlainTime().toString().substring(0, 5);
- toTimeValue = stopDate.toZonedDateTimeISO(currentTimeZone).toPlainTime().toString().substring(0, 5);
- date = startDate.toZonedDateTimeISO(currentTimeZone).toPlainDate().toString();
- }
- };
-</script>
-
-<div class="grid gap-xs">
- <div class="col-4">
- <label for="date"
- class="form-label margin-bottom-xxs">Date</label>
- <input type="date"
- id="date"
- class="form-control width-100%"
- bind:value={date}>
- {#if dateError}
- <small class="color-error">{dateError}</small>
- {/if}
- </div>
- <div class="col-4">
- <label for="from"
- class="form-label margin-bottom-xxs">From</label>
- <input id="from"
- class="form-control width-100%"
- pattern="[0-9][0-9]:[0-9][0-9]"
- type="time"
- bind:value={fromTimeValue}
- on:input={handle_from_time_changed}
- />
- {#if fromTimeError}
- <small class="color-error">{fromTimeError}</small>
- {/if}
- </div>
- <div class="col-4">
- <label for="to"
- class="form-label margin-bottom-xxs">To</label>
- <input id="to"
- class="form-control width-100%"
- pattern="[0-9][0-9]:[0-9][0-9]"
- type="time"
- bind:value={toTimeValue}
- on:input={handle_to_time_changed}
- />
- {#if toTimeError}
- <small class="color-error">{toTimeError}</small>
- {/if}
- </div>
-</div>
diff --git a/apps/projects-web/src/app/pages/views/entry-form/sections/labels.svelte b/apps/projects-web/src/app/pages/views/entry-form/sections/labels.svelte
deleted file mode 100644
index 06c703d..0000000
--- a/apps/projects-web/src/app/pages/views/entry-form/sections/labels.svelte
+++ /dev/null
@@ -1,65 +0,0 @@
-<script>
- import {generate_random_hex_color} from "$shared/lib/colors";
- import labels, {reload_labels, create_label_async} from "$app/lib/stores/labels";
- import Dropdown from "$shared/components/dropdown.svelte";
-
- let labelsError = "";
- let loading = false;
- let DropdownExports;
-
- function reset() {
- DropdownExports.reset();
- console.log("Reset labels-part");
- }
-
- function get_selected() {
- return $labels.filter((c) => Object.hasOwn(c, "selected") && c.selected === true);
- }
-
- function select_label(id) {
- DropdownExports.select(id);
- }
-
- function select_labels(ids) {
- for (const id of ids) {
- DropdownExports.select(id);
- }
- }
-
- async function on_create({name}) {
- loading = true;
- const response = await create_label_async({
- name: name,
- color: generate_random_hex_color(),
- });
- loading = false;
- if (response.ok) {
- // Small pause to allow loading state to update everywhere.
- setTimeout(() => select_label(response.data.id), 50);
- }
- }
-
- export const functions = {
- get_selected,
- reset,
- load_labels: reload_labels,
- select_labels,
- select_label,
- };
-</script>
-
-<Dropdown
- entries={$labels}
- label="Labels"
- maxlength="50"
- createable={true}
- placeholder="Search or create"
- multiple="{true}"
- id="labels-search"
- name="labels-search"
- on_create_async={on_create}
- noResultsText="No labels available (Create a new one by searching for it and pressing enter)"
- errorText="{labelsError}"
- bind:this={DropdownExports}
- {loading}
-/>
diff --git a/apps/projects-web/src/app/pages/views/profile-modal.svelte b/apps/projects-web/src/app/pages/views/profile-modal.svelte
deleted file mode 100644
index 839b59d..0000000
--- a/apps/projects-web/src/app/pages/views/profile-modal.svelte
+++ /dev/null
@@ -1,156 +0,0 @@
-<script>
- import {update_profile} from "$shared/lib/api/user";
- import Modal from "$shared/components/modal.svelte";
- import Alert from "$shared/components/alert.svelte";
- import Button from "$shared/components/button.svelte";
- import {is_email} from "$shared/lib/helpers";
- import {api_base} from "$shared/lib/configuration";
- import {delete_user} from "$app/lib/services/user-service";
- import {get_session_data} from "$shared/lib/session";
-
- const archiveLink = api_base("_/api/account/archive");
-
- let modal;
- let understands = false;
-
- let formIsLoading = false;
- let formError;
-
- let username = get_session_data()?.profile.username;
- let usernameFieldMessage;
- let usernameFieldMessageClass = "color-error";
-
- let password;
- let passwordFieldMessage;
- let passwordFieldMessageClass = "color-error";
-
- async function submit_form(e) {
- e.preventDefault();
- if (!username && !password) {
- console.error("Not submitting becuase both values is empty");
- return;
- }
-
- usernameFieldMessage = "";
- passwordFieldMessage = "";
-
- if (username && !is_email(username)) {
- usernameFieldMessage = "Username has to be a valid email";
- return;
- }
-
- if (password && password?.length < 6) {
- passwordFieldMessage = "The new password must contain at least 6 characters";
- return;
- }
-
- formIsLoading = true;
-
- const response = await update_profile({
- username,
- password,
- });
-
- formIsLoading = false;
-
- if (response.ok) {
- if (password) {
- passwordFieldMessage = "Successfully updated";
- passwordFieldMessageClass = "color-success";
- password = "";
- }
- if (username) {
- usernameFieldMessage = "Successfully updated";
- usernameFieldMessageClass = "color-success";
- password = "";
- }
- } else {
- formError = response.data.title ?? "An unknown error occured";
- }
- }
-
- async function handle_delete_account_button_click() {
- if (understands && confirm("Are you absolutely sure that you want to delete your account?")) {
- await delete_user();
- }
- }
-
- export const functions = {
- open() {
- modal.open();
- },
- close() {
- // modal.close();
- },
- };
-</script>
-
-<Modal title="Profile"
- bind:functions={modal}>
- <section class="margin-bottom-md">
- <p class="text-md margin-bottom-sm">Update your information</p>
- <form on:submit={submit_form}
- autocomplete="new-password">
- {#if formError}
- <small class="color-danger">{formError}</small>
- {/if}
- <div class="margin-bottom-sm">
- <label for="email"
- class="form-label margin-bottom-xxs">New username</label>
- <input type="email"
- class="form-control width-100%"
- id="email"
- placeholder={username}
- bind:value={username}/>
- {#if usernameFieldMessage}
- <small class={usernameFieldMessageClass}>{usernameFieldMessage}</small>
- {/if}
- </div>
- <div class="margin-bottom-sm">
- <label for="password"
- class="form-label margin-bottom-xxs">New password</label>
- <input type="password"
- class="form-control width-100%"
- id="password"
- bind:value={password}/>
- {#if passwordFieldMessage}
- <small class={passwordFieldMessageClass}>{passwordFieldMessage}</small>
- {/if}
- </div>
- <div class="flex justify-end">
- <Button text="Save"
- on:click={submit_form}
- variant="primary"
- loading={formIsLoading}/>
- </div>
- </form>
- </section>
- <section class="margin-bottom-md">
- <p class="text-md margin-bottom-sm">Download your data</p>
- <a class="btn btn--subtle"
- href={archiveLink}
- download>Click here to download your data</a>
- </section>
- <section>
- <p class="text-md margin-bottom-sm">Delete account</p>
- <div class="margin-bottom-sm">
- <Alert
- message="Deleting your account and data means that all of your data (entries, categories, etc.) will be unrecoverable forever.<br>You should probably download your data before continuing."
- type="info"
- />
- </div>
- <div class="form-check margin-bottom-sm">
- <input type="checkbox"
- class="checkbox"
- id="the-consequences"
- bind:checked={understands}/>
- <label for="the-consequences">I understand the consequences of deleting my account and data.</label>
- </div>
- <div class="flex justify-end">
- <Button text="Delete everything"
- variant="accent"
- disabled={!understands}
- on:click={handle_delete_account_button_click}/>
- </div>
- </section>
-</Modal>
diff --git a/apps/projects-web/src/app/pages/views/settings-categories-tile.svelte b/apps/projects-web/src/app/pages/views/settings-categories-tile.svelte
deleted file mode 100644
index 890609a..0000000
--- a/apps/projects-web/src/app/pages/views/settings-categories-tile.svelte
+++ /dev/null
@@ -1,127 +0,0 @@
-<script>
- import {IconNames} from "$shared/lib/configuration";
- import {onMount} from "svelte";
- import {
- delete_time_category,
- get_time_categories,
- } from "$shared/lib/api/time-entry";
- import Button from "$shared/components/button.svelte";
- import Tile from "$shared/components/tile.svelte";
- import {Table, THead, TBody, TCell, TRow} from "$shared/components/table";
-
- let is_loading = true;
- let categories = [];
-
- $: active_categories = categories.filter(c => !c.archived);
- $: archived_categories = categories.filter(c => c.archived);
-
- async function load_categories() {
- is_loading = true;
- const response = await get_time_categories();
- if (response.status === 200) {
- categories = response.data;
- } else if (response.status === 204) {
- categories = [];
- console.log("Empty response when getting time categories");
- } else {
- categories = [];
- console.error("Error when getting time categories");
- }
- is_loading = false;
- }
-
- async function handle_edit_category_click(event) {
- }
-
- async function handle_delete_category_click(event) {
- const row = event.target.closest("tr");
- if (
- row &&
- row.dataset.id &&
- confirm(
- "Are you sure you want to delete this category?\nThis will delete all relating entries!"
- )
- ) {
- const response = await delete_time_category(row.dataset.id);
- if (response.ok) {
- // svelte errors if we remove the row.
- row.classList.add("d-none");
- }
- }
- }
-
- onMount(() => {
- load_categories();
- });
-</script>
-
-<Tile class="col-6@md col-12 {is_loading ? 'c-disabled loading' : ''}">
- <h2 class="margin-bottom-xxs">Categories</h2>
- {#if active_categories.length > 0 && archived_categories.length > 0}
- <nav class="s-tabs text-sm">
- <ul class="s-tabs__list">
- <li><a class="s-tabs__link s-tabs__link--current"
- href="#0">Active ({active_categories.length})</a></li>
- <li><a class="s-tabs__link"
- href="#0">Archived ({archived_categories.length})</a></li>
- </ul>
- </nav>
- {/if}
- <div class="max-width-100% overflow-auto">
- <Table class="text-sm width-100%">
- <THead class="text-left">
- <TCell type="th"
- thScope="col">
- Name
- </TCell>
- <TCell type="th"
- thScope="col">
- Color
- </TCell>
- <TCell type="th"
- thScope="col"
- style="width:50px"></TCell>
- </THead>
- <TBody class="text-left">
- {#if categories.length > 0}
- {#each categories as category}
- <TRow class="text-nowrap"
- data-id={category.id}>
- <TCell>
- {category.name}
- </TCell>
- <TCell>
- <span style="border-left: 3px solid {category.color}; background-color:{category.color}25;">
- {category.color}
- </span>
- </TCell>
- <TCell>
- <Button icon="{IconNames.pencilSquare}"
- variant="reset"
- icon_width="1.2rem"
- class="hide"
- icon_height="1.2rem"
- on:click={handle_edit_category_click}
- title="Edit entry"/>
- <Button icon="{IconNames.trash}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={handle_delete_category_click}
- title="Delete entry"/>
-
- </TCell>
- </TRow>
- {/each}
- {:else}
- <TRow>
- <TCell type="th"
- thScope="3">
- No categories
- </TCell>
- </TRow>
- {/if}
- </TBody>
- </Table>
- </div>
-</Tile>
diff --git a/apps/projects-web/src/app/pages/views/settings-labels-tile.svelte b/apps/projects-web/src/app/pages/views/settings-labels-tile.svelte
deleted file mode 100644
index f59e233..0000000
--- a/apps/projects-web/src/app/pages/views/settings-labels-tile.svelte
+++ /dev/null
@@ -1,112 +0,0 @@
-<script>
- import {IconNames} from "$shared/lib/configuration";
- import {onMount} from "svelte";
- import labels, {reload_labels, delete_label_async} from "$app/lib/stores/labels";
- import Button from "$shared/components/button.svelte";
- import Tile from "$shared/components/tile.svelte";
- import {Table, THead, TBody, TCell, TRow} from "$shared/components/table";
-
- let is_loading = true;
-
- $: active_labels = $labels.filter(c => !c.archived);
- $: archived_labels = $labels.filter(c => c.archived);
-
- async function load_labels() {
- is_loading = true;
- await reload_labels();
- is_loading = false;
- }
-
- async function handle_edit_label_click(event) {
- }
-
- async function handle_delete_label_click(event) {
- const row = event.target.closest("tr");
- if (
- row &&
- row.dataset.id &&
- confirm(
- "Are you sure you want to delete this label?\nIt will be removed from all related entries!"
- )
- ) {
- await delete_label_async({id: row.dataset.id});
- row.classList.add("d-none");
- }
- }
-
- onMount(() => {
- load_labels();
- });
-</script>
-
-<Tile class="col-6@md col-12 {is_loading ? 'c-disabled loading' : ''}">
- <h2 class="margin-bottom-xxs">Labels</h2>
- {#if active_labels.length > 0 && archived_labels.length > 0}
- <nav class="s-tabs text-sm">
- <ul class="s-tabs__list">
- <li><a class="s-tabs__link s-tabs__link--current"
- href="#0">Active ({active_labels.length})</a></li>
- <li><a class="s-tabs__link"
- href="#0">Archived ({archived_labels.length})</a></li>
- </ul>
- </nav>
- {/if}
- <div class="max-width-100% overflow-auto">
- <Table class="text-sm width-100%">
- <THead class="text-left">
- <TCell type="th"
- thScope="row">
- Name
- </TCell>
- <TCell type="th"
- thScope="row">
- Color
- </TCell>
- <TCell type="th"
- thScope="row"
- style="width: 50px;">
- </TCell>
- </THead>
- <TBody class="text-left">
- {#if $labels.length > 0}
- {#each $labels as label}
- <TRow class="text-nowrap"
- dataId={label.id}>
- <TCell>
- {label.name}
- </TCell>
- <TCell>
- <span style="border-left: 3px solid {label.color}; background-color:{label.color}25;">
- {label.color}
- </span>
- </TCell>
- <TCell>
- <Button icon="{IconNames.pencilSquare}"
- variant="reset"
- icon_width="1.2rem"
- class="hide"
- icon_height="1.2rem"
- on:click={handle_edit_label_click}
- title="Edit entry"/>
- <Button icon="{IconNames.trash}"
- variant="reset"
- icon_width="1.2rem"
- icon_height="1.2rem"
- on:click={handle_delete_label_click}
- title="Delete entry"/>
- </TCell>
- </TRow>
- {/each}
- {:else}
- <TRow>
- <TCell type="th"
- thScope="row"
- colspan="3">
- No labels
- </TCell>
- </TRow>
- {/if}
- </TBody>
- </Table>
- </div>
-</Tile>
diff --git a/apps/projects-web/src/index.html b/apps/projects-web/src/index.html
deleted file mode 100644
index 985b62b..0000000
--- a/apps/projects-web/src/index.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<!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/projects-web/src/package.json b/apps/projects-web/src/package.json
deleted file mode 100644
index 8ff516d..0000000
--- a/apps/projects-web/src/package.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "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/projects-web/src/pnpm-lock.yaml b/apps/projects-web/src/pnpm-lock.yaml
deleted file mode 100644
index 3b56115..0000000
--- a/apps/projects-web/src/pnpm-lock.yaml
+++ /dev/null
@@ -1,769 +0,0 @@
-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/projects-web/src/tsconfig.json b/apps/projects-web/src/tsconfig.json
deleted file mode 100644
index c60fce6..0000000
--- a/apps/projects-web/src/tsconfig.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "include": [
- "./**/*.d.ts",
- "./**/*.ts",
- "./**/*.js",
- "./**/*.svelte"
- ],
- "exclude": [
- "./node_modules"
- ],
- "compilerOptions": {
- "target": "esnext",
- "useDefineForClassFields": true,
- "module": "esnext",
- "moduleResolution": "node",
- "allowJs": true,
- "checkJs": false,
- "paths": {
- "$app/*": [
- "./app/*"
- ],
- "$shared/*": [
- "../../web-shared/src/*"
- ]
- }
- }
-}
diff --git a/apps/projects-web/src/vite.config.ts b/apps/projects-web/src/vite.config.ts
deleted file mode 100644
index ac44266..0000000
--- a/apps/projects-web/src/vite.config.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-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"),
- "$public": path.resolve(__dirname, "./_public"),
- }
- },
- build: {
- outDir: "build",
- emptyOutDir: true,
- rollupOptions: {
- input: {
- main: path.resolve(__dirname, "index.html"),
- }
- }
- },
-
- plugins: [
- svelte({
- preprocess: sveltePreprocess()
- })
- ],
-});