diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8da02c9 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +VITE_BACKEND_URL=http://127.0.0.1:3003 diff --git a/.eslintrc.cjs b/.eslintrc.cjs index a96210a..0894ad7 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -16,6 +16,7 @@ module.exports = { ], parserOptions: { extraFileExtensions: ['.svelte'], + sourceType: 'module', }, overrides: [ { diff --git a/.gitignore b/.gitignore index adadc60..31ab9b4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,8 @@ /cypress/screenshots /src/locales /dump.txt +.env +.env.local # Editor directories and files .DS_Store diff --git a/README.md b/README.md index b376d08..be0fc06 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,13 @@ This folder will contain the Web-UI for displaying and comparing historical run ### [Issues](https://github.com/idrinth-api-bench/issues) This is the issue repository for a typescript framework meant to performance test anything even remotely rest-like and related tools. + +## Setup + +- Make sure you're using node.js 20 or higher +- Run `npm install` to install all dependencies +- Run `npm run language` to build the language files +- Create a file .env or .env.local with the following content (replace the localhost placeholder with your backend url): +```env + VITE_BACKEND_URL=http://localhost:3003 +``` diff --git a/gitCommitDev.sh b/gitCommitDev.sh deleted file mode 100644 index d719ec2..0000000 --- a/gitCommitDev.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -# Commit changes form for easy commits - -echo "" -echo "Task types:" -echo "" -echo "build: Changes that affect the build system or external dependencies" -echo "ci: Changes to CI configuration files and scripts" -echo "docs: Documentation changes" -echo "feature: A new feature" -echo "bug: A bug fix" -echo "refactor: A code change that neither fixes a bug nor adds a feature" -echo "" -read -rp "Enter your task type: " task - -echo "" -echo "Enter your task scope:" -echo "" -echo "framework" -echo "documentation-website" -echo "history-microservice" -echo "history-website" -echo "dockerfiles" -echo "examples" -echo "" -read -rp "Enter the task scope: " scope - -echo "" -read -rp "Enter the task summary: " summary -echo "" -read -rp "Enter the task description: " description -echo "" -read -rp "What issue number does this address? " issue -echo "" - -git add . -git commit -m "$task($scope): $summary \n $description \n closes #$issue" -git pull -git push - -echo 'Commits made to your working branch. Happy coding!' diff --git a/index.html b/index.html index d5f74fe..ccd2354 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,20 @@ - + - - - Vite + Svelte + TS + + + History Website for @idrinth-api-bench - +
- + diff --git a/language/de.yml b/language/de.yml index 6b5695f..0c6a0c9 100644 --- a/language/de.yml +++ b/language/de.yml @@ -3,3 +3,10 @@ overview: meta: title: "Projekte" description: "Alle Projekte in einer einfach zu verstehenden Übersicht." +login: + title: "Anmelden" + username: "Benutzername" + submit: "Anmelden" + meta: + title: "Anmelden" + description: "Melden Sie sich bei Ihrem Konto an." diff --git a/language/en.yml b/language/en.yml index 0d808af..cadf928 100644 --- a/language/en.yml +++ b/language/en.yml @@ -1,5 +1,33 @@ overview: - title: "Projects" + title: 'Projects' meta: - title: "Projects" - description: "All your projects in an easy to grasp overview." + title: 'Projects' + description: 'All your projects in an easy to grasp overview.' +login: + username: 'Username' + submit: 'Login' + title: 'Login' + meta: + title: 'Login' + description: 'Login to your account.' +projects: + title: 'Projects' + description: 'A line graph per project to compare their global performance(user choice of Average or Median) + a bar graph with the error count' + meta: + title: 'Projects' +project: + title: 'Project' + description: 'A line graph per route in the project to compare their global performance(user choice of Average or Median) + a bar graph with the error count' + meta: + title: 'Project' + routes: 'Routes' +route: + title: 'Route' + description: 'A line graph per mean and average in the route to compare their and a bar graph with the error count' + meta: + title: 'Route' +global-performance-chart: + title: 'Global Performance' + mean: 'Mean' + average: 'Average' +loading: 'Loading...' diff --git a/language/it.yml b/language/it.yml index 3ef288b..ef636ab 100644 --- a/language/it.yml +++ b/language/it.yml @@ -1,5 +1,33 @@ overview: - title: "Progetti" + title: 'Progetti' meta: - title: "Progetti" + title: 'Progetti' description: "Tutti i tuoi progetti in un'unica semplice interfaccia." +login: + title: 'Accedi' + username: 'Username' + submit: 'Accedi' + meta: + title: 'Accedi' + description: 'Accedi al tuo account.' +projects: + title: 'Progetti' + description: 'Un grafico a linea per progetto per comparare la loro performance globale + un grafico a barre con il conteggio degli errori.' + meta: + title: 'Progetti' +project: + title: 'Progetto' + description: 'Un grafico a linea per rotta per comparare la loro performance globale + un grafico a barre con il conteggio degli errori.' + meta: + title: 'Progetto' + routes: 'Rotte' +route: + title: 'Rotta' + description: 'Un grafico a linea per mediana e media per la rotta per comparare la loro performance e un grafico a barre con il conteggio degli errori.' + meta: + title: 'Rotta' +global-performance-chart: + title: 'Performance Globale' + mean: 'Mediana' + average: 'Media' +loading: 'Caricamento...' diff --git a/package-lock.json b/package-lock.json index ce97cf5..6ec9d94 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,8 +7,13 @@ "": { "name": "@idrinth-api-bench/history-website", "version": "2.7.4", + "dependencies": { + "chart.js": "^4.4.2", + "svelte-chartjs": "^3.1.5" + }, "devDependencies": { "@idrinth-api-bench/assets": "https://github.com/idrinth-api-bench/assets", + "@idrinth-api-bench/chartjs-plugin-stdev-filler": "^1.0.5", "@idrinth/typescript-language-from-yaml": "^1.3.0", "@sveltejs/vite-plugin-svelte": "^3.1.1", "@tsconfig/svelte": "^5.0.2", @@ -21,6 +26,7 @@ "svelte": "^4.2.18", "svelte-check": "^3.8.0", "svelte-eslint-parser": "^0.37.0", + "svelte-routing": "^2.13.0", "typescript": "^5.2.2", "vite": "^5.2.13" }, @@ -32,7 +38,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -539,6 +544,16 @@ "resolved": "git+ssh://git@github.com/idrinth-api-bench/assets.git#eada675b182fff362f11956921d79453770ece3a", "dev": true }, + "node_modules/@idrinth-api-bench/chartjs-plugin-stdev-filler": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@idrinth-api-bench/chartjs-plugin-stdev-filler/-/chartjs-plugin-stdev-filler-1.0.5.tgz", + "integrity": "sha512-KfNw6Z6Bsi73tdvqqH17Z7bKUIIya8cO3gy1KUp+OO1qxvD8Ru4d4q2Nr41JDOGDgMdKktthDukrfsxq6ViAQg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "chart.js": "^4.4" + } + }, "node_modules/@idrinth/typescript-language-from-yaml": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@idrinth/typescript-language-from-yaml/-/typescript-language-from-yaml-1.3.0.tgz", @@ -599,7 +614,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -613,7 +627,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -622,7 +635,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -630,19 +642,23 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -970,8 +986,7 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" }, "node_modules/@types/mdast": { "version": "3.0.15", @@ -1156,7 +1171,6 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -1261,7 +1275,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, "dependencies": { "dequal": "^2.0.3" } @@ -1316,7 +1329,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", - "dev": true, "dependencies": { "dequal": "^2.0.3" } @@ -1369,12 +1381,13 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -1434,6 +1447,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chart.js": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", + "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1493,7 +1518,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", "@types/estree": "^1.0.1", @@ -1681,7 +1705,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" @@ -1782,7 +1805,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "engines": { "node": ">=6" } @@ -2199,7 +2221,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, "dependencies": { "@types/estree": "^1.0.0" } @@ -2306,10 +2327,11 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2793,7 +2815,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "dev": true, "dependencies": { "@types/estree": "*" } @@ -3109,8 +3130,7 @@ "node_modules/locate-character": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", - "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", - "dev": true + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==" }, "node_modules/locate-path": { "version": "6.0.0", @@ -3180,7 +3200,6 @@ "version": "0.30.10", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", - "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -3498,8 +3517,7 @@ "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" }, "node_modules/mdurl": { "version": "2.0.0", @@ -4433,7 +4451,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dev": true, "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^3.0.0", @@ -5475,7 +5492,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5789,7 +5805,6 @@ "version": "4.2.18", "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz", "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==", - "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", "@jridgewell/sourcemap-codec": "^1.4.15", @@ -5810,6 +5825,16 @@ "node": ">=16" } }, + "node_modules/svelte-chartjs": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/svelte-chartjs/-/svelte-chartjs-3.1.5.tgz", + "integrity": "sha512-ka2zh7v5FiwfAX1oMflZ0HkNkgjHjFqANgRyC+vNYXfxtx2ku68Zo+2KgbKeBH2nS1ThDqkIACPzGxy4T0UaoA==", + "license": "MIT", + "peerDependencies": { + "chart.js": "^3.5.0 || ^4.0.0", + "svelte": "^4.0.0" + } + }, "node_modules/svelte-check": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.0.tgz", @@ -5933,6 +5958,13 @@ } } }, + "node_modules/svelte-routing": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/svelte-routing/-/svelte-routing-2.13.0.tgz", + "integrity": "sha512-/NTxqTwLc7Dq306hARJrH2HLXOBtKd7hu8nxgoFDlK0AC4SOKnzisiX/9m8Uksei1QAWtlAEdF91YphNM8iDMg==", + "dev": true, + "license": "MIT" + }, "node_modules/table-layout": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", @@ -5993,6 +6025,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -6005,6 +6038,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } diff --git a/package.json b/package.json index 52777c1..1eb15cd 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "devDependencies": { "@idrinth-api-bench/assets": "https://github.com/idrinth-api-bench/assets", + "@idrinth-api-bench/chartjs-plugin-stdev-filler": "^1.0.5", "@idrinth/typescript-language-from-yaml": "^1.3.0", "@sveltejs/vite-plugin-svelte": "^3.1.1", "@tsconfig/svelte": "^5.0.2", @@ -37,11 +38,16 @@ "svelte": "^4.2.18", "svelte-check": "^3.8.0", "svelte-eslint-parser": "^0.37.0", + "svelte-routing": "^2.13.0", "typescript": "^5.2.2", "vite": "^5.2.13" }, "engines": { "node": ">=20" }, - "engineStrict": true + "engineStrict": true, + "dependencies": { + "chart.js": "^4.4.2", + "svelte-chartjs": "^3.1.5" + } } diff --git a/src/App.svelte b/src/App.svelte index 1f265fd..38ecf23 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,26 +1,7 @@ -
-
- -
-

{t('overview.title',)}

- -
-
-
- - + + \ No newline at end of file diff --git a/src/app.css b/src/app.css index 617f5e9..fa41c65 100644 --- a/src/app.css +++ b/src/app.css @@ -11,25 +11,31 @@ text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + + --outer-padding: 2rem; +} + +body { + margin: 0; + min-height: 100vh; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: var(--outer-padding); } a { - font-weight: 500; color: #646cff; + font-weight: 500; text-decoration: inherit; } + a:hover { color: #535bf2; } -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; -} - h1 { font-size: 3.2em; line-height: 1.1; @@ -39,13 +45,6 @@ h1 { padding: 2em; } -#app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - button { border-radius: 8px; border: 1px solid transparent; @@ -57,9 +56,11 @@ button { cursor: pointer; transition: border-color 0.25s; } + button:hover { border-color: #646cff; } + button:focus, button:focus-visible { outline: 4px auto -webkit-focus-ring-color; @@ -70,9 +71,11 @@ button:focus-visible { color: #213547; background-color: #ffffff; } + a:hover { color: #747bff; } + button { background-color: #f9f9f9; } diff --git a/src/components/Breadcrumbs.svelte b/src/components/Breadcrumbs.svelte new file mode 100644 index 0000000..de54c64 --- /dev/null +++ b/src/components/Breadcrumbs.svelte @@ -0,0 +1,53 @@ + + + + + \ No newline at end of file diff --git a/src/components/GlobalPerformanceLineChart.svelte b/src/components/GlobalPerformanceLineChart.svelte new file mode 100644 index 0000000..8e57411 --- /dev/null +++ b/src/components/GlobalPerformanceLineChart.svelte @@ -0,0 +1,49 @@ + + +

{t('global-performance-chart.title')}

+ +{#if data} + +{:else} +

{t('loading')}

+{/if} diff --git a/src/components/Header.svelte b/src/components/Header.svelte new file mode 100644 index 0000000..1ac8298 --- /dev/null +++ b/src/components/Header.svelte @@ -0,0 +1,23 @@ + + +
+ + + + {#if $user} + + {/if} +
+ + diff --git a/src/components/Logo.svelte b/src/components/Logo.svelte new file mode 100644 index 0000000..5ad7293 --- /dev/null +++ b/src/components/Logo.svelte @@ -0,0 +1,21 @@ + + + + + + + diff --git a/src/components/ProjectPerformanceLineChart.svelte b/src/components/ProjectPerformanceLineChart.svelte new file mode 100644 index 0000000..0ae808a --- /dev/null +++ b/src/components/ProjectPerformanceLineChart.svelte @@ -0,0 +1,50 @@ + + +

{t('global-performance-chart.title')}

+ +{#if data} + +{:else} +

{t('loading')}

+{/if} diff --git a/src/components/RoutePerformanceLineChart.svelte b/src/components/RoutePerformanceLineChart.svelte new file mode 100644 index 0000000..ed004dd --- /dev/null +++ b/src/components/RoutePerformanceLineChart.svelte @@ -0,0 +1,76 @@ + + +

{t('global-performance-chart.title')}

+{#if data} + +{:else} +

{t('loading')}

+{/if} diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 0da72ea..f86d822 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,2 +1,3 @@ export const FIRST_ELEMENT = 0; export const SECOND_ELEMENT = 1; +export const LOCAL_STORAGE_USER_KEY = 'user'; diff --git a/src/lib/http-client.ts b/src/lib/http-client.ts new file mode 100644 index 0000000..016e988 --- /dev/null +++ b/src/lib/http-client.ts @@ -0,0 +1,66 @@ +import { user } from "../stores"; +import { LOCAL_STORAGE_USER_KEY } from "./constants"; +import type { ProjectResponse, ProjectsResponse, RouteResponse, User } from "./response-types"; + +/** + * Generic fetch function + */ +type FetcherOptions = { + method: "GET" | "POST" | "PUT" | "DELETE", + body?: any, + headers?: HeadersInit, +} +export const fetcher = async (url: string, options?: FetcherOptions): Promise => { + const headers = new Headers(options?.headers); + if (localStorage.getItem(LOCAL_STORAGE_USER_KEY)) { + const token = JSON.parse(localStorage.getItem(LOCAL_STORAGE_USER_KEY) || "{}")?.token; + headers.set("Authorization", `Bearer ${ token }`); + } + const response = await fetch( + `${ import.meta.env.VITE_BACKEND_URL }/${ url }`, + { + ...options, + headers, + } + ); + if (! response.ok) { + throw new Error(`Failed to fetch: ${response.status} ${response.statusText}`); + } + return await response.json(); +} + +export const login = async (username: string): Promise => { + const response = await fetcher('login', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username }), + }); + const _user = { + token: response.token, + } + localStorage.setItem(LOCAL_STORAGE_USER_KEY, JSON.stringify(_user)); + user.set(_user); +} + +export const logout = (): void => { + localStorage.removeItem(LOCAL_STORAGE_USER_KEY); + user.set(null); +} + +/** + * Projects+Routes + */ + +export const getProjects = async (): Promise => { + return await fetcher("projects"); +} + +export const getProject = async (projectKey: string): Promise => { + return await fetcher(`project/${ projectKey }/routes`); +} + +export const getRoute = async (projectKey: string, route: string): Promise => { + return await fetcher(`project/${ projectKey }/route/${ route }`); +} diff --git a/src/lib/response-types.ts b/src/lib/response-types.ts new file mode 100644 index 0000000..a3f2d07 --- /dev/null +++ b/src/lib/response-types.ts @@ -0,0 +1,49 @@ +/** + * User + */ +export type User = { + token: string, +} + +/** + * Projects + */ + +export type ProjectMetrics = { + mean: number, + average: number, + stdev: number, +} + +export type ProjectResponse = { + [endpoint: string]: { + [date: string]: ProjectMetrics + }[] +} + +export type ProjectsResponse = { + [projectKey: string]: { + [date: string]: ProjectMetrics + }[] +} + +/** + * Routes + */ +export type RouteResponse = { + [date: string]: { + errors: number, + msgs: { [msg: string]: number }, + count: number, + stdv100: number, + stdv80: number, + avg100: number, + median100: number, + min100: number, + max100: number, + avg80: number, + median80: number, + min80: number, + max80: number + }[] +} diff --git a/src/locales/de-overview.ts b/src/locales/de-overview.ts deleted file mode 100644 index 7eee030..0000000 --- a/src/locales/de-overview.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint max-len:0 */ -const lang = { - title: 'Projekte', - meta: { - title: 'Projekte', - description: 'Alle Projekte in einer einfach zu verstehenden Übersicht.', - }, -}; - -export default lang; diff --git a/src/locales/de.ts b/src/locales/de.ts deleted file mode 100644 index d584ff1..0000000 --- a/src/locales/de.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint max-len:0 */ -const lang = { - overview: { - title: 'Projekte', - meta: { - title: 'Projekte', - description: 'Alle Projekte in einer einfach zu verstehenden Übersicht.', - }, - }, -}; - -export default lang; diff --git a/src/locales/en-overview.ts b/src/locales/en-overview.ts deleted file mode 100644 index 4878a49..0000000 --- a/src/locales/en-overview.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint max-len:0 */ -const lang = { - title: 'Projects', - meta: { - title: 'Projects', - description: 'All your projects in an easy to grasp overview.', - }, -}; - -export default lang; diff --git a/src/locales/en.ts b/src/locales/en.ts deleted file mode 100644 index 7f94df8..0000000 --- a/src/locales/en.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* eslint max-len:0 */ -const lang = { - overview: { - title: 'Projects', - meta: { - title: 'Projects', - description: 'All your projects in an easy to grasp overview.', - }, - }, -}; - -export default lang; diff --git a/src/locales/files.ts b/src/locales/files.ts deleted file mode 100644 index c0b90d4..0000000 --- a/src/locales/files.ts +++ /dev/null @@ -1,5 +0,0 @@ -const files = [ - 'de-overview', - 'en-overview', -]; -export default files; diff --git a/src/locales/language-key.ts b/src/locales/language-key.ts deleted file mode 100644 index efca2a0..0000000 --- a/src/locales/language-key.ts +++ /dev/null @@ -1,3 +0,0 @@ -/* eslint max-len:0 */ -type lk = 'overview.title'|'overview.meta.title'|'overview.meta.description'; -export type languageKey = lk; diff --git a/src/locales/languages.ts b/src/locales/languages.ts deleted file mode 100644 index 1423e2c..0000000 --- a/src/locales/languages.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint max-len:0 */ -const languages = [ - 'de', - 'en', -]; -export default languages; diff --git a/src/locales/translations.ts b/src/locales/translations.ts deleted file mode 100644 index f68552f..0000000 --- a/src/locales/translations.ts +++ /dev/null @@ -1,7 +0,0 @@ -import de_overview from './de-overview.js'; -import en_overview from './en-overview.js'; -const translations = { - 'de-overview': de_overview, - 'en-overview': en_overview, -}; -export default translations; diff --git a/src/main.ts b/src/main.ts index a91e873..9c94b84 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,6 @@ import './app.css'; import App from './App.svelte'; const app = new App({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error target: document.getElementById('app',), },); diff --git a/src/pages/Login.svelte b/src/pages/Login.svelte new file mode 100644 index 0000000..ed35d73 --- /dev/null +++ b/src/pages/Login.svelte @@ -0,0 +1,31 @@ + + + diff --git a/src/pages/Project.svelte b/src/pages/Project.svelte new file mode 100644 index 0000000..13116a2 --- /dev/null +++ b/src/pages/Project.svelte @@ -0,0 +1,33 @@ + + +
+

{t('project.title')}: {project}

+

{t('project.description')}

+ +

{t('project.routes')}

+
    + {#if projectMetricsResponse} + {#each Object.keys(projectMetricsResponse) as route} +
  • + {route} +
  • + {/each} + {/if} +
+ + {#if projectMetricsResponse} + + {/if} +
diff --git a/src/pages/Projects.svelte b/src/pages/Projects.svelte new file mode 100644 index 0000000..3525328 --- /dev/null +++ b/src/pages/Projects.svelte @@ -0,0 +1,25 @@ + + + +
+

{t('projects.title')}

+

{t('projects.description')}

+ +
    + {#each Object.keys(projectMetricsResponse) as project} +
  • + {project} +
  • + {/each} +
+ + +
diff --git a/src/pages/Route.svelte b/src/pages/Route.svelte new file mode 100644 index 0000000..6ea1e79 --- /dev/null +++ b/src/pages/Route.svelte @@ -0,0 +1,23 @@ + + +
+

{t('route.title')}: {project} : {route}

+

{t('route.description')}

+ + {#if routeMetricsResponse} + + {/if} +
diff --git a/src/routes/AppRouter.svelte b/src/routes/AppRouter.svelte new file mode 100644 index 0000000..9224e45 --- /dev/null +++ b/src/routes/AppRouter.svelte @@ -0,0 +1,27 @@ + + + +
+ + + + + + + + + + + + + + diff --git a/src/routes/PrivateRoutes.svelte b/src/routes/PrivateRoutes.svelte new file mode 100644 index 0000000..aba6251 --- /dev/null +++ b/src/routes/PrivateRoutes.svelte @@ -0,0 +1,12 @@ + + + + + + + diff --git a/src/routes/RouteGuard.svelte b/src/routes/RouteGuard.svelte new file mode 100644 index 0000000..10e96de --- /dev/null +++ b/src/routes/RouteGuard.svelte @@ -0,0 +1,17 @@ + + +{#if $user} + +{/if} diff --git a/src/stores.ts b/src/stores.ts new file mode 100644 index 0000000..39c0099 --- /dev/null +++ b/src/stores.ts @@ -0,0 +1,7 @@ +import { type Writable, writable } from 'svelte/store' +import { LOCAL_STORAGE_USER_KEY } from "./lib/constants"; +import type { User } from './lib/response-types'; + +export const user: Writable = writable( + localStorage.getItem(LOCAL_STORAGE_USER_KEY) ? JSON.parse(localStorage.getItem(LOCAL_STORAGE_USER_KEY) || 'null') : null +)