Compare commits

..

34 Commits
1.4.7 ... 1.5.1

Author SHA1 Message Date
9abf74d6d2 🚀RELEASE v1.5.1 2024-11-21 09:46:27 +01:00
50e81a6cb5 fix(dockerfile): AS casing 2024-11-21 09:45:51 +01:00
8fae1fb6b3 chore(deps): bump some 2024-11-21 09:45:39 +01:00
372fa110ec fix(scanstations): CopyScanStationTokenModal open after create 2024-11-21 09:40:32 +01:00
35bec9fe58 chore(deps): pnpm@9 2024-11-21 09:33:22 +01:00
93d67bdba9 chore(deps): node:23.2.0 2024-11-21 09:32:58 +01:00
a5e72a18e3 refactor(scanstations/CopyScanStationTokenModal): drop dispatch 2024-11-21 09:31:45 +01:00
91d2f46b93 fix(config): add explicit window.config 2024-11-21 09:31:09 +01:00
c60bae4533 fix(tracks/AddTrackModal): i18n 2024-11-21 09:30:52 +01:00
43ac878d44 fix: tailwind config 2024-11-21 09:30:24 +01:00
ceabd06a43 🚀RELEASE v1.5.0 2024-11-20 19:29:18 +01:00
9bfc0c5338 fix(components): Add missing toast imports 2024-11-20 19:28:07 +01:00
fb8206ff13 feat(ci)!: Switch to woodpecker
All checks were successful
ci/woodpecker/push/build Pipeline was successful
2023-11-06 19:51:01 +01:00
dceb0ef461 🚀RELEASE v1.4.13
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2023-07-31 18:55:53 +02:00
88bc1982ca Show donations as euro in export 2023-07-31 18:55:09 +02:00
e741a9d7e7 Merge branch 'dev' of git.odit.services:lfk/frontend into dev
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-18 21:35:28 +02:00
65f1d22205 🚀RELEASE v1.4.12 2023-05-18 21:35:16 +02:00
d867c08aba fix(donation/payment): Funny javascript number to float conversion where integers were needed 2023-05-18 21:34:41 +02:00
6193eff38e new license file version [CI SKIP] 2023-05-10 11:29:03 +00:00
f1929e7cf9 Merge branch 'dev' of git.odit.services:lfk/frontend into dev
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-10 13:28:43 +02:00
373484c242 🚀RELEASE v1.4.11 2023-05-10 13:28:34 +02:00
f77460bb0c chore(deps): Lockfile 2023-05-10 13:28:21 +02:00
574e0dcb05 feat(orgs): Show total distance 2023-05-10 13:27:47 +02:00
7b19a0aa08 chore(deps): More bumps 2023-05-10 13:22:11 +02:00
08642d7618 new license file version [CI SKIP] 2023-05-10 11:21:43 +00:00
c3e9c27cd3 🚀RELEASE v1.4.10
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-10 13:21:10 +02:00
29a2854671 chore(deps): Bumped svelte-table 2023-05-10 13:20:44 +02:00
8e6786e722 chore(deps): Pin and bump 2023-05-10 13:19:25 +02:00
6ad40564e3 chore(deps): Bumped scanclientjs 2023-05-10 13:18:44 +02:00
776973bfe9 🚀RELEASE v1.4.9
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-09 16:32:00 +02:00
6025e43baa Fixed empty return 2023-05-09 16:29:41 +02:00
d9a47f882c Changed the in table replacement method 2023-05-09 16:29:25 +02:00
4235758a6d 🚀RELEASE v1.4.8
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-09 10:44:04 +02:00
59fe2dfabb Switched donor loading to non-paginated 2023-05-09 10:43:33 +02:00
30 changed files with 3289 additions and 3488 deletions

View File

@@ -1,101 +0,0 @@
---
kind: secret
name: docker_username
get:
path: odit-registry-builder
name: username
---
kind: secret
name: docker_password
get:
path: odit-registry-builder
name: password
---
kind: secret
name: git_ssh
get:
path: odit-git-bot
name: sshkey
---
kind: secret
name: npm_url
get:
path: odit-npm-cache
name: url
---
kind: pipeline
type: kubernetes
name: build:dev
steps:
- name: run full license export
depends_on: ["clone"]
image: registry.odit.services/hub/library/node:19.7.0-alpine3.16
commands:
- npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8
- pnpm i
- pnpm licenses:export
environment:
NPM_REGISTRY_URL:
from_secret: npm_url
- name: push new licenses file to repo
depends_on: ["run full license export"]
image: appleboy/drone-git-push
settings:
branch: dev
commit: true
commit_message: new license file version [CI SKIP]
author_email: bot@odit.services
remote: git@git.odit.services:lfk/frontend.git
ssh_key:
from_secret: git_ssh
- name: build dev
depends_on: ["clone"]
image: registry.odit.services/library/drone-kaniko
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
build_args:
- NPM_REGISTRY_URL:
from_secret: npm_url
repo: lfk/frontend
tags:
- dev
cache: true
registry: registry.odit.services
trigger:
branch:
- dev
event:
- push
---
kind: pipeline
type: kubernetes
name: build:tags
steps:
- name: build $DRONE_TAG
depends_on: ["clone"]
image: registry.odit.services/library/drone-kaniko
settings:
username:
from_secret: docker_username
password:
from_secret: docker_password
build_args:
- NPM_REGISTRY_URL:
from_secret: npm_url
repo: lfk/frontend
tags:
- "${DRONE_TAG}"
cache: true
registry: registry.odit.services
trigger:
event:
- tag

42
.woodpecker/build.yml Normal file
View File

@@ -0,0 +1,42 @@
steps:
- name: run full license export
image: registry.odit.services/hub/library/node:19.7.0-alpine3.16
commands:
- npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8
- pnpm i
- pnpm licenses:export
secrets:
- source: odit-npm-cache-url
target: NPM_REGISTRY_URL
- name: push new licenses file to repo
image: appleboy/drone-git-push
settings:
branch: dev
commit: true
commit_message: new license file version [CI SKIP]
author_email: bot@odit.services
remote: git@git.odit.services:lfk/frontend.git
ssh_key:
from_secret: odit-git-bot-sshkey
- name: build edge
image: woodpeckerci/plugin-docker-buildx
settings:
repo: registry.odit.services/odit/website
tags:
- edge
registry: registry.odit.services
platforms: linux/amd64,linux/arm64
cache_from: registry.odit.services/odit/website:edge
username:
from_secret: odit-registry-builder-username
password:
from_secret: odit-registry-builder-password
secrets:
- source: odit-npm-cache-url
target: NPM_REGISTRY_URL
when:
event:
- push
branch:
- dev

20
.woodpecker/release.yml Normal file
View File

@@ -0,0 +1,20 @@
steps:
- name: build tag
image: woodpeckerci/plugin-docker-buildx
settings:
repo: registry.odit.services/lfk/frontend
tags:
- "${CI_COMMIT_TAG}"
registry: registry.odit.services
platforms: linux/amd64,linux/arm64
cache_from: registry.odit.services/lfk/frontend:latest
username:
from_secret: odit-registry-builder-username
password:
from_secret: odit-registry-builder-password
secrets:
- source: odit-npm-cache-url
target: NPM_REGISTRY_URL
when:
event:
- tag

View File

@@ -2,10 +2,82 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [1.5.1](https://git.odit.services/lfk/frontend/compare/1.5.0...1.5.1)
- chore(deps): pnpm@9 [`35bec9f`](https://git.odit.services/lfk/frontend/commit/35bec9fe584b93cd52e8bab4e469713468a67f70)
- chore(deps): bump some [`8fae1fb`](https://git.odit.services/lfk/frontend/commit/8fae1fb6b3e033f789d2568cbd2640c0d163dc53)
- fix(scanstations): CopyScanStationTokenModal open after create [`372fa11`](https://git.odit.services/lfk/frontend/commit/372fa110ec402dae166a302f2209c79353983148)
- fix(config): add explicit window.config [`91d2f46`](https://git.odit.services/lfk/frontend/commit/91d2f46b934bcba1429bd1d96e772c25c42a3e28)
- fix(dockerfile): AS casing [`50e81a6`](https://git.odit.services/lfk/frontend/commit/50e81a6cb5773381e153cbec3bed7db820ced84a)
- refactor(scanstations/CopyScanStationTokenModal): drop dispatch [`a5e72a1`](https://git.odit.services/lfk/frontend/commit/a5e72a18e368b5a7ee7b4e1894de613ecb767f28)
- chore(deps): node:23.2.0 [`93d67bd`](https://git.odit.services/lfk/frontend/commit/93d67bdba90a67b45d8895d9facaf66e908d53d6)
- fix(tracks/AddTrackModal): i18n [`c60bae4`](https://git.odit.services/lfk/frontend/commit/c60bae45334c2aa90d8931da07691c196469da46)
- fix: tailwind config [`43ac878`](https://git.odit.services/lfk/frontend/commit/43ac878d44b556c6d7811610f6fe0c9a5eff305f)
#### [1.5.0](https://git.odit.services/lfk/frontend/compare/1.4.13...1.5.0)
> 20 November 2024
- feat(ci)!: Switch to woodpecker [`fb8206f`](https://git.odit.services/lfk/frontend/commit/fb8206ff130f4f65dcf619a2a786e7d5895b77a1)
- 🚀RELEASE v1.5.0 [`ceabd06`](https://git.odit.services/lfk/frontend/commit/ceabd06a4319c3c9ffab680f909730d5bd789540)
- fix(components): Add missing toast imports [`9bfc0c5`](https://git.odit.services/lfk/frontend/commit/9bfc0c5338933e832d5df50457c7978c026d8df6)
#### [1.4.13](https://git.odit.services/lfk/frontend/compare/1.4.12...1.4.13)
> 31 July 2023
- 🚀RELEASE v1.4.13 [`dceb0ef`](https://git.odit.services/lfk/frontend/commit/dceb0ef46197dc56e29c5f52a5bd8f9fe9b70b27)
- Show donations as euro in export [`88bc198`](https://git.odit.services/lfk/frontend/commit/88bc1982cab4481e2e9245f81eff27e095b66a0f)
- new license file version [CI SKIP] [`6193eff`](https://git.odit.services/lfk/frontend/commit/6193eff38e1a9d5726bc7d572ab36b921de843d0)
#### [1.4.12](https://git.odit.services/lfk/frontend/compare/1.4.11...1.4.12)
> 18 May 2023
- 🚀RELEASE v1.4.12 [`65f1d22`](https://git.odit.services/lfk/frontend/commit/65f1d222050b0dec81fc847c1921b6135a55ce50)
- fix(donation/payment): Funny javascript number to float conversion where integers were needed [`d867c08`](https://git.odit.services/lfk/frontend/commit/d867c08aba234d3a7fe9e2311d37dc5e96fc2afc)
- new license file version [CI SKIP] [`08642d7`](https://git.odit.services/lfk/frontend/commit/08642d7618faeae31f0acfe776642c9fa156e5ff)
#### [1.4.11](https://git.odit.services/lfk/frontend/compare/1.4.10...1.4.11)
> 10 May 2023
- chore(deps): Lockfile [`f77460b`](https://git.odit.services/lfk/frontend/commit/f77460bb0c8ce6d0f3d83a077017d5fc7bf55af7)
- 🚀RELEASE v1.4.11 [`373484c`](https://git.odit.services/lfk/frontend/commit/373484c2424bea7ae0d70d342e0ae2076aab1b6a)
- feat(orgs): Show total distance [`574e0dc`](https://git.odit.services/lfk/frontend/commit/574e0dcb051305bde2fc76d8456a35baec0cf309)
- chore(deps): More bumps [`7b19a0a`](https://git.odit.services/lfk/frontend/commit/7b19a0aa08bb6c89c51d27c0d05777e8fcfdad17)
#### [1.4.10](https://git.odit.services/lfk/frontend/compare/1.4.9...1.4.10)
> 10 May 2023
- chore(deps): Bumped svelte-table [`29a2854`](https://git.odit.services/lfk/frontend/commit/29a2854671b3af5b85ea96d050a9076f47b6575d)
- 🚀RELEASE v1.4.10 [`c3e9c27`](https://git.odit.services/lfk/frontend/commit/c3e9c27cd3d4b916f1661d4958cabab038918587)
- chore(deps): Pin and bump [`8e6786e`](https://git.odit.services/lfk/frontend/commit/8e6786e72227b3f07cc805f0957d5b7fd123ec13)
- chore(deps): Bumped scanclientjs [`6ad4056`](https://git.odit.services/lfk/frontend/commit/6ad40564e3e342046f6ee19fab9e455ec3bbff9b)
#### [1.4.9](https://git.odit.services/lfk/frontend/compare/1.4.8...1.4.9)
> 9 May 2023
- 🚀RELEASE v1.4.9 [`776973b`](https://git.odit.services/lfk/frontend/commit/776973bfe9b34c26a1c80d5e458cc2644dd9036b)
- Changed the in table replacement method [`d9a47f8`](https://git.odit.services/lfk/frontend/commit/d9a47f882c1c6bcf98ef85d50d70c010d54b326e)
- Fixed empty return [`6025e43`](https://git.odit.services/lfk/frontend/commit/6025e43baa8516657a60a1de9a82c2189221c6ac)
#### [1.4.8](https://git.odit.services/lfk/frontend/compare/1.4.7...1.4.8)
> 9 May 2023
- Switched donor loading to non-paginated [`59fe2df`](https://git.odit.services/lfk/frontend/commit/59fe2dfabb224863876c4db31a965c34a51a9369)
- 🚀RELEASE v1.4.8 [`4235758`](https://git.odit.services/lfk/frontend/commit/4235758a6d1499715287d6ab193cc87c68d5742e)
#### [1.4.7](https://git.odit.services/lfk/frontend/compare/1.4.6...1.4.7) #### [1.4.7](https://git.odit.services/lfk/frontend/compare/1.4.6...1.4.7)
> 4 May 2023
- Paginated modal data loading [`a8a7711`](https://git.odit.services/lfk/frontend/commit/a8a771114df6eb57d5b1d5497a5be49e619d4102) - Paginated modal data loading [`a8a7711`](https://git.odit.services/lfk/frontend/commit/a8a771114df6eb57d5b1d5497a5be49e619d4102)
- Moved loading to onmount [`4e0a2c8`](https://git.odit.services/lfk/frontend/commit/4e0a2c83015bde5e360c5fb2c0babbeaa03dc2b5) - Moved loading to onmount [`4e0a2c8`](https://git.odit.services/lfk/frontend/commit/4e0a2c83015bde5e360c5fb2c0babbeaa03dc2b5)
- 🚀RELEASE v1.4.7 [`6364536`](https://git.odit.services/lfk/frontend/commit/6364536dcd840c71f7cb6afb31bbc4f160ac4f73)
#### [1.4.6](https://git.odit.services/lfk/frontend/compare/1.4.5...1.4.6) #### [1.4.6](https://git.odit.services/lfk/frontend/compare/1.4.5...1.4.6)

View File

@@ -1,9 +1,9 @@
FROM registry.odit.services/hub/library/node:20.0.0-alpine3.17 as build FROM registry.odit.services/hub/library/node:23.2.0-alpine3.20 AS build
ARG NPM_REGISTRY_URL=https://registry.npmjs.org ARG NPM_REGISTRY_URL=https://registry.npmjs.org
WORKDIR /app WORKDIR /app
COPY package.json pnpm-lock.yaml vite.config.js tailwind.config.js postcss.config.cjs index.html ./ COPY package.json pnpm-lock.yaml vite.config.js tailwind.config.cjs postcss.config.cjs index.html ./
RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8 RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@9
RUN mkdir /pnpm && pnpm config set store-dir /pnpm && pnpm i RUN mkdir /pnpm && pnpm config set store-dir /pnpm && pnpm i
COPY src ./src COPY src ./src
@@ -11,6 +11,6 @@ COPY public ./public
RUN pnpm build RUN pnpm build
# final image # final image
FROM registry.odit.services/library/nginx-brotli:3.15 as final FROM registry.odit.services/library/nginx-brotli:3.15 AS final
COPY --from=build /app/dist /usr/share/nginx/html COPY --from=build /app/dist /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/nginx.conf COPY ./nginx.conf /etc/nginx/nginx.conf

View File

@@ -13,7 +13,7 @@
<body> <body>
<span style="display: none; visibility: hidden" id="buildinfo" <span style="display: none; visibility: hidden" id="buildinfo"
>RELEASE_INFO-1.4.7-RELEASE_INFO</span >RELEASE_INFO-1.5.1-RELEASE_INFO</span
> >
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<script src="/env.js"></script> <script src="/env.js"></script>

View File

@@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-frontend", "name": "@odit/lfk-frontend",
"version": "1.4.7", "version": "1.5.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
@@ -12,16 +12,16 @@
}, },
"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.2.0",
"@sveltejs/vite-plugin-svelte": "2.1.1", "@sveltejs/vite-plugin-svelte": "2.1.1",
"auto-changelog": "2.4.0", "auto-changelog": "2.5.0",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.20",
"postcss": "8.4.23", "postcss": "8.4.49",
"prettier": "^2.8.8", "prettier": "3.3.3",
"prettier-plugin-svelte": "^2.10.0", "prettier-plugin-svelte": "3.2.8",
"release-it": "15.10.1", "release-it": "17.10.0",
"svelte-select": "3.17.0", "svelte-select": "3.17.0",
"tailwindcss": "3.3.2", "tailwindcss": "3.4.15",
"vite": "4.3.3" "vite": "4.3.3"
}, },
"release-it": { "release-it": {
@@ -42,11 +42,11 @@
} }
}, },
"dependencies": { "dependencies": {
"@odit/lfk-client-js": "1.1.1", "@odit/lfk-client-js": "1.1.3",
"@paralleldrive/cuid2": "^2.2.0", "@paralleldrive/cuid2": "2.2.2",
"@tanstack/svelte-table": "^8.8.6", "@tanstack/svelte-table": "8.9.1",
"bwip-js": "^3.4.0", "bwip-js": "3.4.0",
"check-password-strength": "2.0.7", "check-password-strength": "2.0.10",
"csvtojson": "2.0.10", "csvtojson": "2.0.10",
"localforage": "1.10.0", "localforage": "1.10.0",
"marked": "4.3.0", "marked": "4.3.0",

5565
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,11 @@
const config = { const config = {
baseurl: "http://localhost:4010", baseurl: "http://localhost:4010",
baseurl_documentserver: "http://localhost:4010/documents", baseurl_documentserver: "http://localhost:4010/documents",
documentserver_key: documentserver_key:
"NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe", "NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe",
// optional // optional
default_username: "demo", default_username: "demo",
default_password: "demo", default_password: "demo",
prefersHashRouting: true, prefersHashRouting: true,
}; };
window.config = config;

File diff suppressed because one or more lines are too long

View File

@@ -98,35 +98,22 @@
} }
} }
} }
onMount(()=>{
loadDonorsAndRunnersPaginated();
})
async function loadDonorsAndRunnersPaginated() { onMount(async () => {
let page = 0; toast.loading($_("loading-donors"));
let pagesize = 500; donors = (await DonorService.donorControllerGetAll()).map(
while (page >= 0) { (r) => {
const don = await DonorService.donorControllerGetAll(page, pagesize); return { label: getDonorLabel(r), value: r };
const run = await RunnerService.runnerControllerGetAll(page, pagesize);
if (don.length == 0 && run.length == 0) {
page = -2;
} }
);
donors = donors.concat( runners = (await RunnerService.runnerControllerGetAll()).map(
...run.map((r) => { (r) => {
return { label: getDonorLabel(r), value: r }; return { label: getDonorLabel(r), value: r };
}) }
); );
runners = runners.concat( toast.dismiss();
...run.map((r) => { toast.success($_("all-donors-loaded"));
return { label: getDonorLabel(r), value: r }; });
})
);
dataLoaded = true;
page++;
}
}
</script> </script>
{#if modal_open} {#if modal_open}

View File

@@ -33,7 +33,7 @@
toast.loading($_("updating-donation")); toast.loading($_("updating-donation"));
const editable = Object.assign({}, original_data); const editable = Object.assign({}, original_data);
editable.donor = editable.donor.id; editable.donor = editable.donor.id;
editable.paidAmount = paid_amount_input * 100; editable.paidAmount = Math.round(paid_amount_input * 100);
if (editable.responseType == "DISTANCEDONATION" || editable.runner) { if (editable.responseType == "DISTANCEDONATION" || editable.runner) {
editable.runner = editable.runner.id; editable.runner = editable.runner.id;
DonationService.donationControllerPutDistance( DonationService.donationControllerPutDistance(
@@ -46,7 +46,7 @@
toast.dismiss(); toast.dismiss();
toast.success($_("donation-updated")); toast.success($_("donation-updated"));
dispatch("created", { donation: response }); dispatch("created", { donation: result });
}) })
.catch((err) => { .catch((err) => {
// //
@@ -61,7 +61,7 @@
// //
toast.dismiss(); toast.dismiss();
toast.success($_("donation-updated")); toast.success($_("donation-updated"));
dispatch("created", { donation: response }); dispatch("created", { donation: result });
}) })
.catch((err) => { .catch((err) => {
// //

View File

@@ -6,6 +6,7 @@
DonorService, DonorService,
RunnerService, RunnerService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import toast from 'svelte-french-toast'
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
import Select from "svelte-select"; import Select from "svelte-select";

View File

@@ -195,9 +195,12 @@
payment_modal_open={active_edits.length > 0} payment_modal_open={active_edits.length > 0}
paid_amount_input={(active_edits[0]?.paidAmount || 0) / 100} paid_amount_input={(active_edits[0]?.paidAmount || 0) / 100}
on:created={(event) => { on:created={(event) => {
current_donations[ current_donations = current_donations.map((d)=>{
current_donations.findIndex((d) => d.id === event.detail.donation.id) if(d.id === event.detail.donation.id){
].paidAmount = event.detail.donation.paidAmount; d.paidAmount = event.detail.donation.paidAmount;
}
return d;
})
options.update((options) => ({ options.update((options) => ({
...options, ...options,
data: current_donations, data: current_donations,

View File

@@ -35,7 +35,7 @@
d.firstname, d.firstname,
d.middlename, d.middlename,
d.lastname, d.lastname,
d.paidDonationAmount, (d.paidDonationAmount/100).toFixed(2),
address, address,
]; ];
}); });

View File

@@ -2,6 +2,7 @@
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import { UserGroupService } from "@odit/lfk-client-js"; import { UserGroupService } from "@odit/lfk-client-js";
import toast from 'svelte-french-toast'
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
let data_loaded = false; let data_loaded = false;

View File

@@ -5,6 +5,7 @@
CreatePermission, CreatePermission,
UserGroupService, UserGroupService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import toast from 'svelte-french-toast'
import PromiseError from "../base/PromiseError.svelte"; import PromiseError from "../base/PromiseError.svelte";
export let params; export let params;

View File

@@ -4,6 +4,7 @@
RunnerOrganizationService, RunnerOrganizationService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import toast from 'svelte-french-toast'
import store from "../../store"; import store from "../../store";
import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte"; import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte";
@@ -470,6 +471,11 @@
{/if} {/if}
</div> </div>
{/if} {/if}
<div class="text-sm w-full">
<span class="font-medium text-gray-700">{$_("distance")}</span>
<br />
<span class="text-gray-700">{(original_object.total_distance / 1000).toFixed(2)} km</span>
</div>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -5,6 +5,7 @@
RunnerOrganizationService, RunnerOrganizationService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import toast from 'svelte-french-toast'
import { init } from "@paralleldrive/cuid2"; import { init } from "@paralleldrive/cuid2";
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); const createId = init({ length: 10, fingerprint: "lfk-frontend" });

View File

@@ -12,6 +12,7 @@
import Select from "svelte-select"; import Select from "svelte-select";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
import toast from 'svelte-french-toast'
export let modal_open; export let modal_open;
$: selected_team = undefined; $: selected_team = undefined;

View File

@@ -1,200 +1,199 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { tick } from "svelte";
import bwipjs from "bwip-js";
import toast from "svelte-french-toast";
import { tick, createEventDispatcher } from "svelte"; export let copy_modal_open;
import bwipjs from "bwip-js"; export let new_station;
let valueCopy = null;
let areaDom;
let copied = false;
$: is_qrcode = false;
$: barcode = textToBase64Barcode(new_station.key, is_qrcode);
export let copy_modal_open; function close() {
export let new_station; copy_modal_open = false;
const dispatch = createEventDispatcher(); }
let valueCopy = null; async function copy() {
let areaDom; valueCopy = new_station.key;
let copied = false; await tick();
$: is_qrcode = false; areaDom.focus();
$: barcode = textToBase64Barcode(new_station.key, is_qrcode); areaDom.select();
try {
const successful = document.execCommand("copy");
if (!successful) {
throw new Error();
}
toast($_("copied-token-to-clipboard"));
copied = true;
} catch (err) {
toast.Error($_("error-whyile-copying-to-clipboard"));
}
// we can notify by event or storage about copy status
valueCopy = null;
}
function close() { function textToBase64Barcode(text, is_qrcode) {
copy_modal_open = false; const canvas = document.createElement("canvas");
} let bcid = "code128";
async function copy() { if (is_qrcode) {
valueCopy = new_station.key; bcid = "qrcode";
await tick(); }
areaDom.focus(); let codeconfig = {
areaDom.select(); bcid,
try { text: `${text}`,
const successful = document.execCommand("copy"); scale: 4,
if (!successful) { includetext: true,
throw new Error(); textxalign: "center",
} backgroundcolor: "ffffff",
toast($_("copied-token-to-clipboard")); };
copied = true; if (bcid == "code128") {
} catch (err) { codeconfig.height = 10;
toast.Error($_("error-whyile-copying-to-clipboard")); }
} bwipjs.toCanvas(canvas, codeconfig);
// we can notify by event or storage about copy status return canvas.toDataURL("image/png");
valueCopy = null; }
}
function textToBase64Barcode(text, is_qrcode) {
const canvas = document.createElement("canvas");
let bcid = "code128";
if (is_qrcode) {
bcid = "qrcode";
}
let codeconfig = {
bcid,
text: `${text}`,
scale: 4,
includetext: true,
textxalign: "center",
backgroundcolor: "ffffff",
};
if (bcid == "code128") {
codeconfig.height = 10;
}
bwipjs.toCanvas(canvas, codeconfig);
return canvas.toDataURL("image/png");
}
</script> </script>
{#if copy_modal_open} {#if copy_modal_open}
{#if valueCopy != null} {#if valueCopy != null}
<textarea bind:this={areaDom}>{valueCopy}</textarea> <textarea bind:this={areaDom}>{valueCopy}</textarea>
{/if} {/if}
<div class="fixed z-10 inset-0 overflow-y-auto"> <div class="fixed z-10 inset-0 overflow-y-auto">
<div <div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0" class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
> >
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> <div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div <div
class="absolute inset-0 bg-gray-500 opacity-75" class="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" data-id="modal_backdrop"
/> />
</div> </div>
<span <span
class="hidden sm:inline-block sm:align-middle sm:h-screen" class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span aria-hidden="true">&#8203;</span
> >
<div <div
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full" class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-labelledby="modal-headline" aria-labelledby="modal-headline"
> >
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start"> <div class="sm:flex sm:items-start">
<div <div
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10" class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10"
> >
<svg <svg
class="h-6 w-6 text-blue-600" class="h-6 w-6 text-blue-600"
fill="currentColor" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
><path fill="none" d="M0 0h24v24H0z" /> ><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z"
/></svg /></svg
> >
</div> </div>
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left"> <div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900"> <h3 class="text-lg leading-6 font-medium text-gray-900">
{$_("token")} {$_("token")}
</h3> </h3>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<p class="text-sm text-gray-500"> <p class="text-sm text-gray-500">
{$_( {$_(
"the-scanstations-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again" "the-scanstations-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again"
)} )}
<br /> <br />
{$_("please-copy-the-token-and-store-it-somewhere-save")} {$_("please-copy-the-token-and-store-it-somewhere-save")}
</p> </p>
</div> </div>
<div class="mt-2 mb-6"> <div class="mt-2 mb-6">
<label <label
for="token" for="token"
class="block text-sm font-medium text-gray-700" class="block text-sm font-medium text-gray-700"
>{$_("token")}</label >{$_("token")}</label
> >
<button on:click={copy} class="inline-flex"> <button on:click={copy} class="inline-flex">
<p <p
name="token" name="token"
class:bg-green-200={copied} class:bg-green-200={copied}
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 p-2" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 p-2"
> >
{new_station.key} {new_station.key}
</p> </p>
<div <div
class="bg-gray-200 border-gray-300 border-t border-b border-r text-black rounded-r-md sm:text-sm p-2 mt-1 cursor-pointer" class="bg-gray-200 border-gray-300 border-t border-b border-r text-black rounded-r-md sm:text-sm p-2 mt-1 cursor-pointer"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
width="24" width="24"
height="24" height="24"
><path fill="none" d="M0 0h24v24H0z" /> ><path fill="none" d="M0 0h24v24H0z" />
<path <path
fill="currentColor" fill="currentColor"
d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z" d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z"
/></svg /></svg
> >
</div> </div>
</button> </button>
<p class="text-gray-500 text-xs"> <p class="text-gray-500 text-xs">
{$_("click-to-copy-token-to-clipboard")} {$_("click-to-copy-token-to-clipboard")}
</p> </p>
</div> </div>
</div> </div>
</div> </div>
<div class="mx-auto text-center items-center"> <div class="mx-auto text-center items-center">
<h2 class="text-lg leading-6 font-medium text-gray-900"> <h2 class="text-lg leading-6 font-medium text-gray-900">
{$_("config-codes")} {$_("config-codes")}
</h2> </h2>
<span class="flex items-center text-center"> <span class="flex items-center text-center">
<p class="text-md text-gray-900 mr-3">Format:</p> <p class="text-md text-gray-900 mr-3">Format:</p>
<label for="codeswitch" class="text-md text-gray-900 mr-3" <label for="codeswitch" class="text-md text-gray-900 mr-3"
>Code128</label >Code128</label
> >
<input <input
id="codeswitch" id="codeswitch"
type="checkbox" type="checkbox"
bind:checked={is_qrcode} bind:checked={is_qrcode}
class="relative shrink-0 w-[3.25rem] h-7 bg-gray-100 checked:bg-none checked:bg-blue-600 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 ring-1 ring-transparent focus:border-blue-600 focus:ring-blue-600 ring-offset-white focus:outline-none appearance-none before:inline-block before:w-6 before:h-6 before:bg-white checked:before:bg-blue-200 before:translate-x-0 checked:before:translate-x-full before:shadow before:rounded-full before:transform before:ring-0 before:transition before:ease-in-out before:duration-200" class="relative shrink-0 w-[3.25rem] h-7 bg-gray-100 checked:bg-none checked:bg-blue-600 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 ring-1 ring-transparent focus:border-blue-600 focus:ring-blue-600 ring-offset-white focus:outline-none appearance-none before:inline-block before:w-6 before:h-6 before:bg-white checked:before:bg-blue-200 before:translate-x-0 checked:before:translate-x-full before:shadow before:rounded-full before:transform before:ring-0 before:transition before:ease-in-out before:duration-200"
/> />
<label for="codeswitch" class="text-md text-gray-900 ml-3" <label for="codeswitch" class="text-md text-gray-900 ml-3"
>QR-Code</label >QR-Code</label
> >
</span> </span>
<h3 class="leading-6 font-medium text-gray-900"> <h3 class="leading-6 font-medium text-gray-900">
{$_("api-endpoint")} {$_("api-endpoint")}
</h3> </h3>
<img <img
class:w-[50%]={is_qrcode} class:w-[50%]={is_qrcode}
class:w-full={!is_qrcode} class:w-full={!is_qrcode}
class="md:w-auto mb-2 mx-auto" class="md:w-auto mb-2 mx-auto"
alt="Registrierungscode" alt="Registrierungscode"
src={textToBase64Barcode(window.config.baseurl, is_qrcode)} src={textToBase64Barcode(window.config.baseurl, is_qrcode)}
/> />
<h3 class="leading-6 font-medium text-gray-900">{$_("token")}</h3> <h3 class="leading-6 font-medium text-gray-900">{$_("token")}</h3>
<img <img
class:w-[50%]={is_qrcode} class:w-[50%]={is_qrcode}
class:w-full={!is_qrcode} class:w-full={!is_qrcode}
class="md:w-auto mb-2 mx-auto" class="md:w-auto mb-2 mx-auto"
alt="Registrierungscode" alt="Registrierungscode"
src={barcode} src={barcode}
/> />
</div> </div>
</div> </div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button <button
on:click={close} on:click={close}
type="button" type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-green-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-green-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm"
> >
{$_("yes-i-copied-the-token")} {$_("yes-i-copied-the-token")}
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/if} {/if}

View File

@@ -1,44 +1,229 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import store from "../../store"; import store from "../../store";
import AddScanStationModal from "./AddScanStationModal.svelte"; import { ScanStationService } from "@odit/lfk-client-js";
import CopyScanStationTokenModal from "./CopyScanStationTokenModal.svelte"; import AddScanStationModal from "./AddScanStationModal.svelte";
import ScanStationsOverview from "./ScanStationsOverview.svelte"; import CopyScanStationTokenModal from "./CopyScanStationTokenModal.svelte";
export let modal_open = false; import ScanStationsEmptyState from "./ScanStationsEmptyState.svelte";
export let copy_modal_open = false; import ConfirmScanStationDeletion from "./ConfirmScanStationDeletion.svelte";
export let new_station = {}; import toast from "svelte-french-toast";
let current_stations = []; //
export let modal_open = false;
export let copy_modal_open = false;
export let new_station = {};
//
const promise = ScanStationService.scanStationControllerGetAll().then(
(result) => {
current_stations = result;
}
);
$: searchvalue = "";
$: active_deletes = [];
let delete_station = {};
let current_stations = [];
</script> </script>
<section class="container p-5"> <section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight"> <span class="mb-1 text-3xl font-extrabold leading-tight">
{$_("scanstations")} {$_("scanstations")}
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:CREATE")} {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:CREATE")}
<button <button
on:click={() => { on:click={() => {
modal_open = true; modal_open = true;
}} }}
type="button" type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
> >
{$_("create-a-new-scanstation")} {$_("create-a-new-scanstation")}
</button> </button>
{/if} {/if}
</span> </span>
<ScanStationsOverview <ConfirmScanStationDeletion
bind:current_stations on:cancelDelete={(event) => {
bind:modal_open modal_open = false;
bind:new_station active_deletes[event.detail.id] = false;
bind:copy_modal_open }}
/> bind:modal_open
bind:delete_station
/>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:GET")}
{#await promise}
<div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
role="alert"
>
<p class="font-bold">{$_("scanstations-are-being-loaded")}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div>
{:then}
{#if current_stations.length === 0}
<ScanStationsEmptyState />
{:else}
<input
type="search"
bind:value={searchvalue}
placeholder={$_("datatable.search")}
aria-label={$_("datatable.search")}
class="mb-4"
/>
<div
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
>
<table class="divide-y divide-gray-200 w-full">
<thead class="bg-gray-50">
<tr class="odd:bg-white even:bg-gray-100">
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("track")}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("description")}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("status")}
</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_("action")}</span>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
{#each current_stations as s}
{#if Object.values(s)
.toString()
.toLowerCase()
.includes(searchvalue)}
<tr
class="odd:bg-white even:bg-gray-100"
data-rowid="station_{s.id}"
>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
<a
href="../tracks"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
>
{s.track.name || s.track.distance + "m"}</a
>
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
{s.description}
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
{#if s.enabled}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
>{$_("active")}</span
>
{:else}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800"
>{$_("inactive")}</span
>
{/if}
</div>
</td>
{#if active_deletes[s.id] === true}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<button
on:click={() => {
active_deletes[s.id] = false;
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
>{$_("cancel-delete")}</button
>
<button
on:click={() => {
ScanStationService.scanStationControllerRemove(
s.id,
false
)
.then((resp) => {
current_stations = current_stations.filter(
(obj) => obj.id !== s.id
);
toast($_("station-deleted"));
})
.catch((err) => {
modal_open = true;
delete_station = s;
});
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("confirm-delete")}</button
>
</td>
{:else}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<a
href="/scanstations/{s.id}"
class="text-indigo-600 hover:text-indigo-900"
>{$_("details")}</a
>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:DELETE")}
<button
on:click={() => {
active_deletes[s.id] = true;
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("delete")}</button
>
{/if}
</td>
{/if}
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
{/if}
{:catch error}
<div
class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"
>
<span class="inline-block align-middle mr-8">
<b class="capitalize">{$_("general_promise_error")}</b>
{error}
</span>
</div>
{/await}
{/if}
</section> </section>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:CREATE")} {#if store.state.jwtinfo.userdetails.permissions.includes("STATION:CREATE")}
<AddScanStationModal <AddScanStationModal
bind:modal_open bind:modal_open
bind:current_stations bind:current_stations
bind:new_station bind:new_station
bind:copy_modal_open bind:copy_modal_open
/> />
<CopyScanStationTokenModal bind:copy_modal_open bind:new_station /> <CopyScanStationTokenModal bind:copy_modal_open bind:new_station />
{/if} {/if}

View File

@@ -1,195 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import { ScanStationService } from "@odit/lfk-client-js";
const promise = ScanStationService.scanStationControllerGetAll().then(
(result) => {
current_stations = result;
}
);
import store from "../../store";
import ScanStationsEmptyState from "./ScanStationsEmptyState.svelte";
import ConfirmScanStationDeletion from "./ConfirmScanStationDeletion.svelte";
import toast from "svelte-french-toast";
$: searchvalue = "";
$: active_deletes = [];
let delete_station = {};
let modal_open = false;
export let current_stations = [];
</script>
<ConfirmScanStationDeletion
on:cancelDelete={(event) => {
modal_open = false;
active_deletes[event.detail.id] = false;
}}
bind:modal_open
bind:delete_station
/>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:GET")}
{#await promise}
<div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
role="alert"
>
<p class="font-bold">{$_("scanstations-are-being-loaded")}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div>
{:then}
{#if current_stations.length === 0}
<ScanStationsEmptyState />
{:else}
<input
type="search"
bind:value={searchvalue}
placeholder={$_("datatable.search")}
aria-label={$_("datatable.search")}
class="mb-4"
/>
<div
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
>
<table class="divide-y divide-gray-200 w-full">
<thead class="bg-gray-50">
<tr class="odd:bg-white even:bg-gray-100">
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("track")}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("description")}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
{$_("status")}
</th>
<th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_("action")}</span>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
{#each current_stations as s}
{#if Object.values(s)
.toString()
.toLowerCase()
.includes(searchvalue)}
<tr
class="odd:bg-white even:bg-gray-100"
data-rowid="station_{s.id}"
>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
<a
href="../tracks"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
>
{s.track.name || s.track.distance + "m"}</a
>
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="ml-4">
<div class="text-sm font-medium text-gray-900">
{s.description}
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
{#if s.enabled}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
>{$_("active")}</span
>
{:else}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800"
>{$_("inactive")}</span
>
{/if}
</div>
</td>
{#if active_deletes[s.id] === true}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<button
on:click={() => {
active_deletes[s.id] = false;
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
>{$_("cancel-delete")}</button
>
<button
on:click={() => {
ScanStationService.scanStationControllerRemove(
s.id,
false
)
.then((resp) => {
current_stations = current_stations.filter(
(obj) => obj.id !== s.id
);
toast($_("station-deleted"));
})
.catch((err) => {
modal_open = true;
delete_station = s;
});
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("confirm-delete")}</button
>
</td>
{:else}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"
>
<a
href="/scanstations/{s.id}"
class="text-indigo-600 hover:text-indigo-900"
>{$_("details")}</a
>
{#if store.state.jwtinfo.userdetails.permissions.includes("STATION:DELETE")}
<button
on:click={() => {
active_deletes[s.id] = true;
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("delete")}</button
>
{/if}
</td>
{/if}
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
{/if}
{:catch error}
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
<span class="inline-block align-middle mr-8">
<b class="capitalize">{$_("general_promise_error")}</b>
{error}
</span>
</div>
{/await}
{/if}

View File

@@ -2,6 +2,7 @@
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import isEmail from "validator/es/lib/isEmail"; import isEmail from "validator/es/lib/isEmail";
import { MeService } from "@odit/lfk-client-js"; import { MeService } from "@odit/lfk-client-js";
import toast from 'svelte-french-toast'
import ConfirmProfileDeletion from "./ConfirmProfileDeletion.svelte"; import ConfirmProfileDeletion from "./ConfirmProfileDeletion.svelte";
import PasswordStrength, { import PasswordStrength, {

View File

@@ -4,6 +4,7 @@
import { tick, createEventDispatcher } from "svelte"; import { tick, createEventDispatcher } from "svelte";
export let copy_modal_open; export let copy_modal_open;
export let new_client; export let new_client;
import toast from 'svelte-french-toast'
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let valueCopy = null; let valueCopy = null;
let areaDom; let areaDom;

View File

@@ -5,6 +5,7 @@
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import toast from 'svelte-french-toast'
import store from "../../store"; import store from "../../store";
import Select from "svelte-select"; import Select from "svelte-select";
@@ -290,6 +291,13 @@
on:clear={() => (teamdata.parentGroup = null)} on:clear={() => (teamdata.parentGroup = null)}
/> />
</div> </div>
<div class="text-sm w-full">
<span class="font-medium text-gray-700">{$_("distance")}</span>
<br />
<span class="text-gray-700"
>{(original.total_distance / 1000).toFixed(2)} km</span
>
</div>
</section> </section>
{:else} {:else}
{#await promise} {#await promise}

View File

@@ -223,7 +223,7 @@
type="button" type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm"
> >
Create {$_("create")}
</button> </button>
<button <button
on:click={() => { on:click={() => {

View File

@@ -37,6 +37,7 @@
"all-associated-scans-will-get-deleted-as-well": "Alle Scans dieser Station werden ebenfalls gelöscht", "all-associated-scans-will-get-deleted-as-well": "Alle Scans dieser Station werden ebenfalls gelöscht",
"all-associated-teams-and-runners-will-be-deleted-too": "Alle assoziierten Teams und Läufer werden auch gelöscht!", "all-associated-teams-and-runners-will-be-deleted-too": "Alle assoziierten Teams und Läufer werden auch gelöscht!",
"all-cards-loaded": "Alle Karten geladen", "all-cards-loaded": "Alle Karten geladen",
"all-donors-loaded": "Alle Sponsoren geladen",
"already-paid": "Bereits bezahlt", "already-paid": "Bereits bezahlt",
"amount": "Anzahl", "amount": "Anzahl",
"amount-per-kilometer": "Betrag pro Kilometer", "amount-per-kilometer": "Betrag pro Kilometer",
@@ -270,6 +271,7 @@
"loading-contact-details": "Kontaktdaten werden geladen ...", "loading-contact-details": "Kontaktdaten werden geladen ...",
"loading-donation-details": "Lade Sponsoringdetails", "loading-donation-details": "Lade Sponsoringdetails",
"loading-donor-details": "Lade Details", "loading-donor-details": "Lade Details",
"loading-donors": "Sponsoren werden geladen",
"loading-group-detail": "Lade Gruppendetails...", "loading-group-detail": "Lade Gruppendetails...",
"loading-profile-data": "Lade Profildaten", "loading-profile-data": "Lade Profildaten",
"loading-runners": "Läufer werden geladen...", "loading-runners": "Läufer werden geladen...",

View File

@@ -37,6 +37,7 @@
"all-associated-scans-will-get-deleted-as-well": "All associated scans will get deleted as well", "all-associated-scans-will-get-deleted-as-well": "All associated scans will get deleted as well",
"all-associated-teams-and-runners-will-be-deleted-too": "All associated teams and runners will be deleted too!", "all-associated-teams-and-runners-will-be-deleted-too": "All associated teams and runners will be deleted too!",
"all-cards-loaded": "All cards loaded", "all-cards-loaded": "All cards loaded",
"all-donors-loaded": "All donors loaded",
"already-paid": "Already paid", "already-paid": "Already paid",
"amount": "Amount", "amount": "Amount",
"amount-per-kilometer": "Amount per kilometer", "amount-per-kilometer": "Amount per kilometer",
@@ -270,6 +271,7 @@
"loading-contact-details": "Loading contact details...", "loading-contact-details": "Loading contact details...",
"loading-donation-details": "Loading donation details", "loading-donation-details": "Loading donation details",
"loading-donor-details": "Loading donor details", "loading-donor-details": "Loading donor details",
"loading-donors": "Loading donors",
"loading-group-detail": "Loading group detail...", "loading-group-detail": "Loading group detail...",
"loading-profile-data": "Loading profile data", "loading-profile-data": "Loading profile data",
"loading-runners": "loading runners...", "loading-runners": "loading runners...",