Compare commits

..

No commits in common. "d32eb8266b0e9daec4b9ba52832d5e5118abec45" and "8fb1e0ca0f51c90270fb5e1a05be5e8273238a2c" have entirely different histories.

11 changed files with 291 additions and 44 deletions

View File

@ -1,4 +1,4 @@
FROM registry.odit.services/hub/library/node:20.0.0-alpine3.17 as build FROM registry.odit.services/hub/library/node:19.7.0-alpine3.16 as build
ARG NPM_REGISTRY_URL=https://registry.npmjs.org ARG NPM_REGISTRY_URL=https://registry.npmjs.org
WORKDIR /app WORKDIR /app

View File

@ -2,20 +2,21 @@
## ✒️ Overview ## ✒️ Overview
This is an API client for [https://git.odit.services/lfk/backend](@lfk/backend) This is an API client for [https://git.odit.services/lfk/backend](@lfk/backend)
- WebApp built with [Svelte](https://svelte.dev), [WindiCSS](https://windicss.org/) (to compile [TailwindCSS](https://tailwindcss.com/)) and [Vite](https://vitejs.dev).
This application is intended for use by admin users/ members only. This application is intended for use by admin users/ members only.
## 🚀 Getting Started ## 🚀 Getting Started
``` ```
pnpm i yarn
``` ```
## Development ## Development
``` ```
pnpm dev yarn dev
/ /
pnpm dev --open yarn dev --open
``` ```
## Build ## Build
``` ```
pnpm build yarn build
``` ```

View File

@ -12,14 +12,14 @@
"license": "CC-BY-NC-SA-4.0", "license": "CC-BY-NC-SA-4.0",
"devDependencies": { "devDependencies": {
"@odit/license-exporter": "0.0.12", "@odit/license-exporter": "0.0.12",
"@sveltejs/vite-plugin-svelte": "2.1.0", "@sveltejs/vite-plugin-svelte": "2.0.4",
"auto-changelog": "2.4.0", "auto-changelog": "2.4.0",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.14",
"postcss": "8.4.23", "postcss": "8.4.21",
"release-it": "15.10.1", "release-it": "15.10.1",
"svelte-select": "3.17.0", "svelte-select": "3.17.0",
"tailwindcss": "3.3.1", "tailwindcss": "3.3.1",
"vite": "4.3.1" "vite": "4.2.1"
}, },
"release-it": { "release-it": {
"git": { "git": {
@ -57,6 +57,6 @@
"xlsx": "0.18.5" "xlsx": "0.18.5"
}, },
"volta": { "volta": {
"node": "20.0.0" "node": "19.7.0"
} }
} }

71
pnpm-lock.yaml generated
View File

@ -55,17 +55,17 @@ devDependencies:
specifier: 0.0.12 specifier: 0.0.12
version: 0.0.12 version: 0.0.12
'@sveltejs/vite-plugin-svelte': '@sveltejs/vite-plugin-svelte':
specifier: 2.1.0 specifier: 2.0.4
version: 2.1.0(svelte@3.58.0)(vite@4.3.1) version: 2.0.4(svelte@3.58.0)(vite@4.2.1)
auto-changelog: auto-changelog:
specifier: 2.4.0 specifier: 2.4.0
version: 2.4.0 version: 2.4.0
autoprefixer: autoprefixer:
specifier: 10.4.14 specifier: 10.4.14
version: 10.4.14(postcss@8.4.23) version: 10.4.14(postcss@8.4.21)
postcss: postcss:
specifier: 8.4.23 specifier: 8.4.21
version: 8.4.23 version: 8.4.21
release-it: release-it:
specifier: 15.10.1 specifier: 15.10.1
version: 15.10.1 version: 15.10.1
@ -74,10 +74,10 @@ devDependencies:
version: 3.17.0 version: 3.17.0
tailwindcss: tailwindcss:
specifier: 3.3.1 specifier: 3.3.1
version: 3.3.1(postcss@8.4.23) version: 3.3.1(postcss@8.4.21)
vite: vite:
specifier: 4.3.1 specifier: 4.2.1
version: 4.3.1 version: 4.2.1
packages: packages:
@ -556,8 +556,8 @@ packages:
engines: {node: '>=14.16'} engines: {node: '>=14.16'}
dev: true dev: true
/@sveltejs/vite-plugin-svelte@2.1.0(svelte@3.58.0)(vite@4.3.1): /@sveltejs/vite-plugin-svelte@2.0.4(svelte@3.58.0)(vite@4.2.1):
resolution: {integrity: sha512-Bc9A8mtTGlhTICdLL/aZ+jyHI3kwtkcXremOH5xwjbNNKOTOtY8nMyG8/oZ5KK8IuUfAn1WL58Bp2tofDJBW0w==} resolution: {integrity: sha512-pjqhW00KwK2uzDGEr+yJBwut+D+4XfJO/+bHHdHzPRXn9+1Jeq5JcFHyrUiYaXgHtyhX0RsllCTm4ssAx4ZY7Q==}
engines: {node: ^14.18.0 || >= 16} engines: {node: ^14.18.0 || >= 16}
peerDependencies: peerDependencies:
svelte: ^3.54.0 svelte: ^3.54.0
@ -569,8 +569,8 @@ packages:
magic-string: 0.30.0 magic-string: 0.30.0
svelte: 3.58.0 svelte: 3.58.0
svelte-hmr: 0.15.1(svelte@3.58.0) svelte-hmr: 0.15.1(svelte@3.58.0)
vite: 4.3.1 vite: 4.2.1
vitefu: 0.2.4(vite@4.3.1) vitefu: 0.2.4(vite@4.2.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@ -738,7 +738,7 @@ packages:
- encoding - encoding
dev: true dev: true
/autoprefixer@10.4.14(postcss@8.4.23): /autoprefixer@10.4.14(postcss@8.4.21):
resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
hasBin: true hasBin: true
@ -750,7 +750,7 @@ packages:
fraction.js: 4.2.0 fraction.js: 4.2.0
normalize-range: 0.1.2 normalize-range: 0.1.2
picocolors: 1.0.0 picocolors: 1.0.0
postcss: 8.4.23 postcss: 8.4.21
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
dev: true dev: true
@ -2849,29 +2849,29 @@ packages:
engines: {node: '>= 6'} engines: {node: '>= 6'}
dev: true dev: true
/postcss-import@14.1.0(postcss@8.4.23): /postcss-import@14.1.0(postcss@8.4.21):
resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
peerDependencies: peerDependencies:
postcss: ^8.0.0 postcss: ^8.0.0
dependencies: dependencies:
postcss: 8.4.23 postcss: 8.4.21
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
read-cache: 1.0.0 read-cache: 1.0.0
resolve: 1.22.2 resolve: 1.22.2
dev: true dev: true
/postcss-js@4.0.1(postcss@8.4.23): /postcss-js@4.0.1(postcss@8.4.21):
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
engines: {node: ^12 || ^14 || >= 16} engines: {node: ^12 || ^14 || >= 16}
peerDependencies: peerDependencies:
postcss: ^8.4.21 postcss: ^8.4.21
dependencies: dependencies:
camelcase-css: 2.0.1 camelcase-css: 2.0.1
postcss: 8.4.23 postcss: 8.4.21
dev: true dev: true
/postcss-load-config@3.1.4(postcss@8.4.23): /postcss-load-config@3.1.4(postcss@8.4.21):
resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
peerDependencies: peerDependencies:
@ -2884,17 +2884,17 @@ packages:
optional: true optional: true
dependencies: dependencies:
lilconfig: 2.1.0 lilconfig: 2.1.0
postcss: 8.4.23 postcss: 8.4.21
yaml: 1.10.2 yaml: 1.10.2
dev: true dev: true
/postcss-nested@6.0.0(postcss@8.4.23): /postcss-nested@6.0.0(postcss@8.4.21):
resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==} resolution: {integrity: sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w==}
engines: {node: '>=12.0'} engines: {node: '>=12.0'}
peerDependencies: peerDependencies:
postcss: ^8.2.14 postcss: ^8.2.14
dependencies: dependencies:
postcss: 8.4.23 postcss: 8.4.21
postcss-selector-parser: 6.0.11 postcss-selector-parser: 6.0.11
dev: true dev: true
@ -2910,8 +2910,8 @@ packages:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
dev: true dev: true
/postcss@8.4.23: /postcss@8.4.21:
resolution: {integrity: sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==} resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==}
engines: {node: ^10 || ^12 || >=14} engines: {node: ^10 || ^12 || >=14}
dependencies: dependencies:
nanoid: 3.3.6 nanoid: 3.3.6
@ -3496,7 +3496,7 @@ packages:
resolution: {integrity: sha512-brIBNNB76mXFmU/Kerm4wFnkskBbluBDCjx/8TcpYRb298Yh2dztS2kQ6bhtjMcvUhd5ynClfwpz5h2gnzdQ1A==} resolution: {integrity: sha512-brIBNNB76mXFmU/Kerm4wFnkskBbluBDCjx/8TcpYRb298Yh2dztS2kQ6bhtjMcvUhd5ynClfwpz5h2gnzdQ1A==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
/tailwindcss@3.3.1(postcss@8.4.23): /tailwindcss@3.3.1(postcss@8.4.21):
resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==} resolution: {integrity: sha512-Vkiouc41d4CEq0ujXl6oiGFQ7bA3WEhUZdTgXAhtKxSy49OmKs8rEfQmupsfF0IGW8fv2iQkp1EVUuapCFrZ9g==}
engines: {node: '>=12.13.0'} engines: {node: '>=12.13.0'}
hasBin: true hasBin: true
@ -3517,11 +3517,11 @@ packages:
normalize-path: 3.0.0 normalize-path: 3.0.0
object-hash: 3.0.0 object-hash: 3.0.0
picocolors: 1.0.0 picocolors: 1.0.0
postcss: 8.4.23 postcss: 8.4.21
postcss-import: 14.1.0(postcss@8.4.23) postcss-import: 14.1.0(postcss@8.4.21)
postcss-js: 4.0.1(postcss@8.4.23) postcss-js: 4.0.1(postcss@8.4.21)
postcss-load-config: 3.1.4(postcss@8.4.23) postcss-load-config: 3.1.4(postcss@8.4.21)
postcss-nested: 6.0.0(postcss@8.4.23) postcss-nested: 6.0.0(postcss@8.4.21)
postcss-selector-parser: 6.0.11 postcss-selector-parser: 6.0.11
postcss-value-parser: 4.2.0 postcss-value-parser: 4.2.0
quick-lru: 5.1.1 quick-lru: 5.1.1
@ -3737,8 +3737,8 @@ packages:
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
dev: false dev: false
/vite@4.3.1: /vite@4.2.1:
resolution: {integrity: sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==} resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==}
engines: {node: ^14.18.0 || >=16.0.0} engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -3763,13 +3763,14 @@ packages:
optional: true optional: true
dependencies: dependencies:
esbuild: 0.17.16 esbuild: 0.17.16
postcss: 8.4.23 postcss: 8.4.21
resolve: 1.22.2
rollup: 3.20.2 rollup: 3.20.2
optionalDependencies: optionalDependencies:
fsevents: 2.3.2 fsevents: 2.3.2
dev: true dev: true
/vitefu@0.2.4(vite@4.3.1): /vitefu@0.2.4(vite@4.2.1):
resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==}
peerDependencies: peerDependencies:
vite: ^3.0.0 || ^4.0.0 vite: ^3.0.0 || ^4.0.0
@ -3777,7 +3778,7 @@ packages:
vite: vite:
optional: true optional: true
dependencies: dependencies:
vite: 4.3.1 vite: 4.2.1
dev: true dev: true
/vm2@3.9.16: /vm2@3.9.16:

View File

@ -0,0 +1,57 @@
<script>
export let handler;
let filterValue = "";
</script>
<th>
<input
on:input={() => {
setTimeout(() => {
const v = filterValue.toLowerCase();
handler.filter(v, (c) => {
// if (v === "") {
// return c;
// }
if (!c.runner && v === "blanko") {
return "blanko";
}
if (v.startsWith("#")) {
return `#${c.runner?.id}`;
}
if (c.runner) {
let runnerName = `${c.runner.firstname} ${c.runner.lastname}`;
if (c.runner.middlename) {
runnerName = `${c.runner.firstname} ${c.runner.middlename} ${c.runner.lastname}`;
}
runnerName = runnerName.toLowerCase();
return runnerName;
}
return "";
});
}, 150);
}}
bind:value={filterValue}
type="text"
name="runnerfilter"
id="runnerfilter"
/>
</th>
<style>
th {
border-bottom: 1px solid #e0e0e0;
}
input {
margin: -1px 0 0 0;
padding: 0;
width: 100%;
height: 24px;
border: none;
text-align: left;
background: inherit;
outline: 0;
font-size: 14px;
}
</style>

View File

@ -0,0 +1,45 @@
<script>
import { _ } from "svelte-i18n";
export let handler;
let selected = "all";
</script>
<th>
<select
on:input={() => {
setTimeout(() => {
if (`${selected}`.trim()) {
if (selected === "all") {
handler.filter("", "enabled");
} else {
handler.filter(selected, "enabled");
}
}
}, 50);
}}
bind:value={selected}
name="statusfilter"
id="statusfilter"
>
<option value="all">{$_("all")}</option>
<option value="true">{$_("enabled")}</option>
<option value="false">{$_("disabled")}</option>
</select>
</th>
<style>
th {
border-bottom: 1px solid #e0e0e0;
}
select {
margin: -1px 0 0 0;
padding: 0;
width: 100%;
height: 24px;
border: none;
text-align: left;
background: inherit;
outline: 0;
font-size: 14px;
}
</style>

View File

@ -0,0 +1,35 @@
<script>
import { _ } from "svelte-i18n";
export let groups;
export let handler;
let selected = "all";
</script>
<th style="border-bottom: 1px solid #ddd;">
<select
on:input={() => {
setTimeout(() => {
if (`${selected}`.trim()) {
const value = selected;
handler.filter(value, (runner) => {
if (
runner.group.id === value ||
runner?.group?.parentGroup?.id === value ||
value === "all"
)
return runner;
return "";
});
}
}, 50);
}}
bind:value={selected}
name="groupfilter"
id="groupfilter"
>
<option value="all">{$_('all')}</option>
{#each groups as g}
<option value={g.value}>{g.label}</option>
{/each}
</select>
</th>

View File

@ -0,0 +1,50 @@
<script>
export let handler;
let filterValue = "";
</script>
<th>
<input
on:input={() => {
setTimeout(() => {
const v = filterValue.toLowerCase();
handler.filter(v, (c) => {
if (v.startsWith("#")) {
return `#${c.runner?.id}`;
}
if (c.runner) {
let runnerName = `${c.runner.firstname} ${c.runner.lastname}`;
if (c.runner.middlename) {
runnerName = `${c.runner.firstname} ${c.runner.middlename} ${c.runner.lastname}`;
}
runnerName = runnerName.toLowerCase();
return runnerName;
}
return "";
});
}, 150);
}}
placeholder="Filter"
bind:value={filterValue}
type="text"
name="runnerfilter"
id="runnerfilter"
/>
</th>
<style>
th {
border-bottom: 1px solid #e0e0e0;
}
input {
margin: -1px 0 0 0;
padding: 0;
width: 100%;
height: 24px;
border: none;
text-align: left;
background: inherit;
outline: 0;
font-size: 14px;
}
</style>

View File

@ -0,0 +1,31 @@
<script>
import { _ } from "svelte-i18n";
export let tracks;
export let handler;
let selected = "all";
</script>
<th style="border-bottom: 1px solid #ddd;">
<select
on:input={() => {
setTimeout(() => {
if (`${selected}`.trim()) {
const value = selected;
handler.filter(value, (scan) => {
// TODO: fix filter
if (scan.track.id === value || value === "all") return scan.track.id;
return "";
});
}
}, 50);
}}
bind:value={selected}
name="trackfilter"
id="trackfilter"
>
<option value="all">{$_("all")}</option>
{#each tracks as track}
<option value={track.id}>{track.name}</option>
{/each}
</select>
</th>

View File

@ -103,6 +103,26 @@
<div class="mt-5 md:mt-0 md:col-span-2"> <div class="mt-5 md:mt-0 md:col-span-2">
<div class="shadow sm:rounded-md sm:overflow-hidden"> <div class="shadow sm:rounded-md sm:overflow-hidden">
<div class="px-4 py-5 bg-white space-y-6 sm:p-6"> <div class="px-4 py-5 bg-white space-y-6 sm:p-6">
<div>
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="block text-sm font-medium text-gray-700">
{$_('profile-picture')}
</label>
<div class="mt-2 flex items-center">
<span
class="inline-block h-20 w-20 rounded-full overflow-hidden bg-gray-100">
<img
alt={$_('profile-picture')}
class="h-20 w-20 rounded-full overflow-hidden bg-gray-100"
src={editable.profilePic || 'https://lauf-fuer-kaya.de/lfk-logo.png'} />
</span>
<!-- <button
type="button"
class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Change
</button> -->
</div>
</div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label <label
for="username" for="username"

View File

@ -191,6 +191,13 @@
{/if} {/if}
</span> </span>
</div> </div>
<div class="mt-3 text-sm w-full">
<p class="ml-1 font-medium text-gray-700">{$_('profile-picture')}</p>
<img
alt={$_('profile-picture')}
class="h-20 w-20 rounded-full overflow-hidden bg-gray-100"
src={editable_userdata.profilePic} />
</div>
<div class="mt-3 text-sm w-full"> <div class="mt-3 text-sm w-full">
<label <label
for="enabled" for="enabled"