Compare commits

..

2 Commits
0.16.3 ... main

Author SHA1 Message Date
06a4428835 Merge pull request 'sync - 0.12.3' (#127) from dev into main
Reviewed-on: #127
Reviewed-by: Nicolai Ort <info@nicolai-ort.com>
2021-04-08 17:28:29 +00:00
bbad338ced Merge pull request 'Merge Minor into main' (#113) from dev into main
Reviewed-on: #113
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
2021-03-26 16:34:53 +00:00
61 changed files with 1661 additions and 3295 deletions

View File

@@ -27,7 +27,7 @@ name: build:dev
steps: steps:
- name: run full license export - name: run full license export
depends_on: ["clone"] depends_on: ["clone"]
image: registry.odit.services/hub/library/node:alpine image: node:alpine
commands: commands:
- yarn - yarn
- yarn licenses:export - yarn licenses:export
@@ -43,23 +43,18 @@ steps:
ssh_key: ssh_key:
from_secret: git_ssh from_secret: git_ssh
- name: build dev - name: build dev
depends_on: ["clone"] image: plugins/docker
image: registry.odit.services/library/drone-kaniko depends_on: [clone]
settings: settings:
username: username:
from_secret: docker_username from_secret: docker_username
password: password:
from_secret: docker_password from_secret: docker_password
build_args: repo: registry.odit.services/lfk/frontend
- NPM_REGISTRY_DOMAIN:
from_secret: npmjs_domain
- NPM_REGISTRY_TOKEN:
from_secret: npmjs_token
repo: lfk/frontend
tags: tags:
- dev - dev
cache: true
registry: registry.odit.services registry: registry.odit.services
mtu: 1000
trigger: trigger:
branch: branch:
- dev - dev
@@ -72,23 +67,18 @@ type: kubernetes
name: build:tags name: build:tags
steps: steps:
- name: build $DRONE_TAG - name: build $DRONE_TAG
depends_on: ["clone"] image: plugins/docker
image: registry.odit.services/library/drone-kaniko depends_on: [clone]
settings: settings:
username: username:
from_secret: docker_username from_secret: docker_username
password: password:
from_secret: docker_password from_secret: docker_password
build_args: repo: registry.odit.services/lfk/frontend
- NPM_REGISTRY_DOMAIN:
from_secret: npmjs_domain
- NPM_REGISTRY_TOKEN:
from_secret: npmjs_token
repo: lfk/frontend
tags: tags:
- "${DRONE_TAG}" - '${DRONE_TAG}'
cache: true
registry: registry.odit.services registry: registry.odit.services
mtu: 1000
trigger: trigger:
event: event:
- tag - tag

1
.gitignore vendored
View File

@@ -8,4 +8,3 @@ public/index.html
.yarn .yarn
.pnp.js .pnp.js
.yarnrc.yml .yarnrc.yml
pnpm-lock.yaml

View File

@@ -2,217 +2,8 @@
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.
#### [0.16.3](https://git.odit.services/lfk/frontend/compare/0.16.2...0.16.3)
- Bumped vite build targets [`5fe4763`](https://git.odit.services/lfk/frontend/commit/5fe47634e8980e77b65c05f213c475cf49273609)
- new license file version [CI SKIP] [`a659091`](https://git.odit.services/lfk/frontend/commit/a6590910cfdc5e91fba91c1bc9237e407ef15fd2)
- Merge pull request 'feature/156-pdf_names' (#157) from feature/156-pdf_names into dev [`ad454c3`](https://git.odit.services/lfk/frontend/commit/ad454c386cbf11abc59d41d269d1a0ef7442c9ed)
- Added ids for generated pdfs [`0b2c296`](https://git.odit.services/lfk/frontend/commit/0b2c296de069a4ef302c5535de01bc18236675bc)
#### [0.16.2](https://git.odit.services/lfk/frontend/compare/0.16.1...0.16.2)
> 23 February 2023
- Fixed scanmodal [`#154`](https://git.odit.services/lfk/frontend/issues/154)
- 🚀RELEASE v0.16.2 [`0e85940`](https://git.odit.services/lfk/frontend/commit/0e85940cba292cbccd1ec038aa24f4a719382c19)
- Merge pull request 'feature/147-cardoverview_performance' (#153) from feature/147-cardoverview_performance into dev [`8d479c3`](https://git.odit.services/lfk/frontend/commit/8d479c32f82938904aee6a10deb80fea85487e4b)
- Merge pull request 'Fixed scanmodal' (#155) from bugfix/154-scan_select_runner_by_id into dev [`549785c`](https://git.odit.services/lfk/frontend/commit/549785cf7d1c9edc1be37dc745b97096232a08ca)
- i18n [`ca6da15`](https://git.odit.services/lfk/frontend/commit/ca6da15ef761184a55b18d56f749f660a32cbb83)
- Bsic datatable conversion [`757655e`](https://git.odit.services/lfk/frontend/commit/757655ea63b3667bc4612ae1595eb52910b61137)
- 1st datatable try with @vincjo/datatables [`81b8fbf`](https://git.odit.services/lfk/frontend/commit/81b8fbf4e341e6f2998a6e9e2053972c5c225021)
- Dasboard Cards redesign [`eb1c17e`](https://git.odit.services/lfk/frontend/commit/eb1c17e3ac7e8f5e7310a90421fc9db3ed15c497)
- set .phone to null if empty [`da6dd55`](https://git.odit.services/lfk/frontend/commit/da6dd55d139a672fa50204eabdca67d9740614a0)
- add group filtering to table [`14d64b6`](https://git.odit.services/lfk/frontend/commit/14d64b6070d98e6368da5709e9ff8221e8a621c7)
- formatting [`24d0747`](https://git.odit.services/lfk/frontend/commit/24d074752f1c5dc1a14b075ac14b448d7e129376)
- RunnersOverview loading fix [`2e075ea`](https://git.odit.services/lfk/frontend/commit/2e075eafab5c4d78fd9aa9d66834b477b2685bfc)
- Added old formatting for runner and status [`df63c23`](https://git.odit.services/lfk/frontend/commit/df63c2388da359dec9ed9968bc9f970be7092a45)
- Added custom status filter [`f0a2b28`](https://git.odit.services/lfk/frontend/commit/f0a2b2859fa18426a454b7d9d6dd22dfdfe7ce27)
- Basic checkbox fix [`1337676`](https://git.odit.services/lfk/frontend/commit/1337676e0894c46da0b6dcb7553e5ea8f88d0c14)
- Fixed all filter [`8dfa19f`](https://git.odit.services/lfk/frontend/commit/8dfa19fa0f9897c61342ece956df88731c7aa861)
- improved runner search [`1b0cd5b`](https://git.odit.services/lfk/frontend/commit/1b0cd5b90bcceb92627c6b7cdcdd7792ed81b50f)
- set table-layout:fixed + display when loaded [`65e8998`](https://git.odit.services/lfk/frontend/commit/65e89988943807c1606a8b6aea49564b13d52537)
- Trigger edit modal [`32ddb66`](https://git.odit.services/lfk/frontend/commit/32ddb66fc8d8cd689f1104759812f4cee4b7a613)
- cleaned up table search [`08047a9`](https://git.odit.services/lfk/frontend/commit/08047a93073c32f5dd7a8e958400ae8a5b7f4035)
- Fixed edit update bug [`0feee0a`](https://git.odit.services/lfk/frontend/commit/0feee0ae2fb6d8dba0b6fd72cedc0712dc749511)
- fix: z-index on action buttons [`224034d`](https://git.odit.services/lfk/frontend/commit/224034dcc6263d3b0a8ea20045e435142d8ed2af)
- rename: ThFilterGroup -&gt; ThFilterStatus [`2a6a399`](https://git.odit.services/lfk/frontend/commit/2a6a39916a03c0466e63354e9f5ad7924cb59b6b)
- new license file version [CI SKIP] [`0e5490f`](https://git.odit.services/lfk/frontend/commit/0e5490f1c84217a5a6d5c8745c4667b32ca65e1a)
- new license file version [CI SKIP] [`026d3d4`](https://git.odit.services/lfk/frontend/commit/026d3d41c1b976a4dc7c733576a6a9e8d4b13b78)
- Updated breakpoints [`452d010`](https://git.odit.services/lfk/frontend/commit/452d0101838d72bff7d588a953faae028e2ff819)
- Tailwind bump [`a101873`](https://git.odit.services/lfk/frontend/commit/a101873eb0946b284a11a5081642711f5087da14)
- Fixed checkbox show [`0900c26`](https://git.odit.services/lfk/frontend/commit/0900c2691e4cfe5046e8ae186c8ac8884c90abd6)
- Removed unused console log [`aafc4c8`](https://git.odit.services/lfk/frontend/commit/aafc4c8d62a7a0a493c8bd60149f90c842534bdd)
- i18n import [`6fe134a`](https://git.odit.services/lfk/frontend/commit/6fe134afc8bfef4e7470b7e53b9312b172a7322b)
- Merge pull request 'fix: RunnerDetail: set .phone to null if empty' (#152) from bugfix/151-runnerdetail--cannot-unset-phone-number into dev [`329c1cc`](https://git.odit.services/lfk/frontend/commit/329c1cc037a43c818ba3b6c72581d29586d76232)
- Merge pull request 'feature/146-runner-table-performance-data-table' (#150) from feature/146-runner-table-performance-data-table into dev [`b82d638`](https://git.odit.services/lfk/frontend/commit/b82d638de1aa1f72aada212cf3e4147d808b4fcf)
- Merge pull request 'feature/148-dashboard_statscards' (#149) from feature/148-dashboard_statscards into dev [`fd1a06b`](https://git.odit.services/lfk/frontend/commit/fd1a06b3595b3713ad474e623c74105125602d46)
- Fixed top checkbox state [`3d2acb6`](https://git.odit.services/lfk/frontend/commit/3d2acb692a28c116790248679e238fb562b24ac5)
#### [0.16.1](https://git.odit.services/lfk/frontend/compare/0.16.0...0.16.1)
> 15 February 2023
- fix: donor detail: sponsorings: unset middlename will show as "null" [`#145`](https://git.odit.services/lfk/frontend/issues/145)
- 🚀RELEASE v0.16.1 [`4499480`](https://git.odit.services/lfk/frontend/commit/449948050b8673d43a8dfbb225c3198e4bbb3c7b)
#### [0.16.0](https://git.odit.services/lfk/frontend/compare/0.15.6...0.16.0)
> 3 February 2023
- First page for statsclients [`f299617`](https://git.odit.services/lfk/frontend/commit/f299617c600d2bba7b4405c7c3acae9fd93aefa8)
- 🚀RELEASE v0.16.0 [`75684ef`](https://git.odit.services/lfk/frontend/commit/75684efa1ae0edb4b4d414757c5acf2a77c572e5)
- Basic statsclient detail [`0215860`](https://git.odit.services/lfk/frontend/commit/02158605be824e5ac21a6284731138190988c794)
- Updated Add modal [`f679330`](https://git.odit.services/lfk/frontend/commit/f679330466205e6480cd7f2b7c2b4fdc41c51525)
- Switched drone to kaniko [`1c98005`](https://git.odit.services/lfk/frontend/commit/1c980059cff5c87c452428b53513507c2339451f)
- Re-added copy modal [`fecb07e`](https://git.odit.services/lfk/frontend/commit/fecb07ee373dcaaeaea69fdf8d4c6ee2c257c89c)
- Added Statsclients to sidebar [`068076d`](https://git.odit.services/lfk/frontend/commit/068076dd47373c673a25e730cb8a57c686682810)
- Fixed imports and naming [`f3cc07c`](https://git.odit.services/lfk/frontend/commit/f3cc07c009ed0a34e61f1aad47a1a31778145439)
- new license file version [CI SKIP] [`2c4f27a`](https://git.odit.services/lfk/frontend/commit/2c4f27a943bb35be6728bb49bd5c2263cba78165)
- Merge pull request 'feature/143-beamershow_clients' (#144) from feature/143-beamershow_clients into dev [`53b7dec`](https://git.odit.services/lfk/frontend/commit/53b7dec7cd516c908d45591b855f4be09371f9b1)
- Updated deletion modal [`93fc7c2`](https://git.odit.services/lfk/frontend/commit/93fc7c2e83f78dd88f15d9246127bb9e69f1a8ee)
- Updated mounted variables [`674e6a9`](https://git.odit.services/lfk/frontend/commit/674e6a90ec23dde9377bea64c14a50e41ffa450d)
- Removed Key after creation [`e10c648`](https://git.odit.services/lfk/frontend/commit/e10c6480a504338b21e30fdf2577e5b6c3b635db)
- Updated docker base images [`9767553`](https://git.odit.services/lfk/frontend/commit/976755338b8621064f9a73147aa600af1f77cd51)
- Added translation [`96c55db`](https://git.odit.services/lfk/frontend/commit/96c55db63dbfed92b78ff0e7bdab7a8cce4d76e9)
- Pinned versions [`cff112d`](https://git.odit.services/lfk/frontend/commit/cff112d705a74a135286943298f3f344341325ac)
- Tailwind bump [`e0cbfb0`](https://git.odit.services/lfk/frontend/commit/e0cbfb000bee59a71e06bd58a9c7ef6a0fc7091d)
- Added missing translation [`19a333d`](https://git.odit.services/lfk/frontend/commit/19a333d7bda525fbcb3c68f3cbf85a4f925a9707)
- Bumped apiclient [`c28f1ee`](https://git.odit.services/lfk/frontend/commit/c28f1ee0bc4456595c21858f38e52ed6f16871c5)
- new license file version [CI SKIP] [`3a66f4c`](https://git.odit.services/lfk/frontend/commit/3a66f4c862db9f35c223cc7007b0560fef4e1d63)
- Bumped apiclient [`28cbc5b`](https://git.odit.services/lfk/frontend/commit/28cbc5b98ca09657100e1740b83aa2617243b26b)
- Ignore pnpm lock [`2d8c4c1`](https://git.odit.services/lfk/frontend/commit/2d8c4c1698a1675f618e85e678012f310f87c6ee)
#### [0.15.6](https://git.odit.services/lfk/frontend/compare/0.15.5...0.15.6)
> 19 July 2021
- 🚀RELEASE v0.15.6 [`9fc4ad6`](https://git.odit.services/lfk/frontend/commit/9fc4ad63c4f77b46d645e83c94b51747b91247b8)
- Fixed donations getting reduced to the first one on certificates [`2391668`](https://git.odit.services/lfk/frontend/commit/2391668a25a1e11a1409df572d77ad1635070fbc)
- new license file version [CI SKIP] [`97054a7`](https://git.odit.services/lfk/frontend/commit/97054a71c1ab8a045762a55148124965c6994373)
#### [0.15.5](https://git.odit.services/lfk/frontend/compare/0.15.4...0.15.5)
> 5 July 2021
- 🚀RELEASE v0.15.5 [`717d335`](https://git.odit.services/lfk/frontend/commit/717d33547c3378424dd720005da9952f8a753f1a)
- Merge pull request 'Fixed kilometer conversion' (#142) from bugfix/141-runner_kilometers into dev [`997be32`](https://git.odit.services/lfk/frontend/commit/997be32679dc38c9fb0e92b6ce011057b854d99d)
- Fixed kilometer conversion [`134f00c`](https://git.odit.services/lfk/frontend/commit/134f00c40e0c8252e7604a73151e8d6685b2c61d)
- new license file version [CI SKIP] [`e752ee1`](https://git.odit.services/lfk/frontend/commit/e752ee12d17a4423f4364f7766eafbe7d4cef2d1)
#### [0.15.4](https://git.odit.services/lfk/frontend/compare/0.15.3...0.15.4)
> 5 July 2021
- Merge pull request 'fix total donation sum in dashboard' (#140) from bugfix/139-total-donation-sum-is-wrong into dev [`#139`](https://git.odit.services/lfk/frontend/issues/139)
- 🚀RELEASE v0.15.4 [`cc4515f`](https://git.odit.services/lfk/frontend/commit/cc4515ff66b1c1de3747d0ee6cc465574accedb7)
- divide by 100 + toFixes(2) [`b246f2b`](https://git.odit.services/lfk/frontend/commit/b246f2b349b06d1adea318dfad58f97fb1a249bb)
#### [0.15.3](https://git.odit.services/lfk/frontend/compare/0.15.2...0.15.3)
> 16 April 2021
- 🚀RELEASE v0.15.3 [`76b69d8`](https://git.odit.services/lfk/frontend/commit/76b69d851aa590ecf8caac135b72962a72e83635)
- Small bugfix (null got displayed) 🛠 [`224f586`](https://git.odit.services/lfk/frontend/commit/224f5863683ae2543a4a435510ed2c558dc5d307)
#### [0.15.2](https://git.odit.services/lfk/frontend/compare/0.15.1...0.15.2)
> 16 April 2021
- 🚀RELEASE v0.15.2 [`9add6c8`](https://git.odit.services/lfk/frontend/commit/9add6c8ff1fbeed91fe97a7cf262921b716f4e3c)
- Footer - noopener link [`cee04c1`](https://git.odit.services/lfk/frontend/commit/cee04c1d6fb6005cefe77fb95855ab6fe2cc448f)
- Hotfix: Team change recognition 🐞 [`cbec785`](https://git.odit.services/lfk/frontend/commit/cbec78589d2fa21f12ce87e71bff2b49c3a7d345)
- NGINX cache assets [`e54a480`](https://git.odit.services/lfk/frontend/commit/e54a4807f70bc333396885f81d3dcc7ae6c115d9)
#### [0.15.1](https://git.odit.services/lfk/frontend/compare/0.15.0...0.15.1)
> 16 April 2021
- 🚀RELEASE v0.15.1 [`a85db7c`](https://git.odit.services/lfk/frontend/commit/a85db7cb3f89881794e37a66ecd822f8ad5873f1)
- Merge pull request '🐞🐳 fix Dockerfile' (#138) from bugfix/136-opacity_reactivity into dev [`2bd3779`](https://git.odit.services/lfk/frontend/commit/2bd3779839de16a89b91a3da93033e2a2b742ab7)
- 🚚 move to tailwind [`07ac041`](https://git.odit.services/lfk/frontend/commit/07ac041d69b3b1810e5db538b53fe62084490f7a)
- 🐞🐳 fix Dockerfile [`303e33c`](https://git.odit.services/lfk/frontend/commit/303e33cafb4a1be01e4c4b43f46ff0c651cb4620)
- Dockerfile now uses selfhosted registry [`b4e689d`](https://git.odit.services/lfk/frontend/commit/b4e689dddf0b93a2794aa30ea83e8c6505d6bbfd)
- new license file version [CI SKIP] [`98a0b03`](https://git.odit.services/lfk/frontend/commit/98a0b036c5490b4bc4992e83f3bca02be39927fa)
- Merge pull request 'Opacity import fix bugfix/136-opacity_reactivity' (#137) from bugfix/136-opacity_reactivity into dev [`fb3f30f`](https://git.odit.services/lfk/frontend/commit/fb3f30fb1024de61ce1c541dae90374454f6ef96)
- Added bs import fix [`6213952`](https://git.odit.services/lfk/frontend/commit/621395200751c2d42b9ad44c77e84bda03b62e83)
#### [0.15.0](https://git.odit.services/lfk/frontend/compare/0.14.0...0.15.0)
> 15 April 2021
- 🚀RELEASE v0.15.0 [`5c02028`](https://git.odit.services/lfk/frontend/commit/5c02028841c68d9a284bf6971eec2b6bc2fdf1f3)
- Merge pull request 'Mark donations as payed feature/133-donation_payments' (#135) from feature/133-donation_payments into dev [`c561b53`](https://git.odit.services/lfk/frontend/commit/c561b536705a68215d9c0a6b320587d1647bf57f)
- Sorted translations [`c7a858e`](https://git.odit.services/lfk/frontend/commit/c7a858eed7962294bc9df3c92ce2e46b0a354796)
- Added total donation amount to donor overview [`e42ea94`](https://git.odit.services/lfk/frontend/commit/e42ea943b7821d433fe21599edbd9f76c3128ef2)
- Added Add Payment button to donor overview [`6e6e8b2`](https://git.odit.services/lfk/frontend/commit/6e6e8b26171f16542c101520800b4b6ea7c023d3)
- You can now open a modal to add a payment to a donation from the donation overview [`a943aaf`](https://git.odit.services/lfk/frontend/commit/a943aaf5fce8f113dd967d3842e2b0d7d50604e9)
- You can now add payments from the donation overview [`1dbab03`](https://git.odit.services/lfk/frontend/commit/1dbab03fe73b5e0fc011f9b0af7199bd71bc79c5)
- Added payment updating via detail [`bdcf5d3`](https://git.odit.services/lfk/frontend/commit/bdcf5d3fc08d250377226a253642d79b2e82d624)
- Added **all** missing toast translations [`de5aa92`](https://git.odit.services/lfk/frontend/commit/de5aa9237d261b5d47a8def35afa7f8e0089aea6)
- You can now mark fixed donations as already paid on creation [`3d3a10a`](https://git.odit.services/lfk/frontend/commit/3d3a10aafb16d371be9471eb5172f9251fb2583f)
- Added translations 🌎 [`d015f97`](https://git.odit.services/lfk/frontend/commit/d015f9739570c44a7a2fe6ba248c9a45c3047c62)
- Changed top info style for donation overview [`4c2c24a`](https://git.odit.services/lfk/frontend/commit/4c2c24af2ca5c2874a583b0fd93bee147a17f449)
- Added paid donation amount and status to donation detail [`5645eea`](https://git.odit.services/lfk/frontend/commit/5645eeaafaa4254edf1a81bc597ce0c7a9b03ff0)
- Added total donation amount to donation overview [`961477d`](https://git.odit.services/lfk/frontend/commit/961477d5224bc44b552d2fc2851d8514116f4e20)
- Fixed chante recognition bug for fixed donation [`0f0aae7`](https://git.odit.services/lfk/frontend/commit/0f0aae7ba4cf5dfab15d56ce48edbdbc7cb7e403)
- Added total donation amount to donor detail [`a5f7101`](https://git.odit.services/lfk/frontend/commit/a5f71015a6557d664e9d3f505613352792fc38cb)
- Added msiisng runner id conversion [`5761815`](https://git.odit.services/lfk/frontend/commit/57618156b49b2b0f0274f2126fef36a017d90022)
- AddDonationModal - vertical alignment for paid status [`18acac8`](https://git.odit.services/lfk/frontend/commit/18acac83bc6532e14d36b3399d867e026d0c88ac)
- Added missing updated comparison [`04a3038`](https://git.odit.services/lfk/frontend/commit/04a3038369f2717c43459318b7b5754ebbaa9e45)
- DonationsOverview contrast on action [`d7d4447`](https://git.odit.services/lfk/frontend/commit/d7d44470bb08ac06594bc400608c17eeacb0434b)
- Fixed typo [`4c0886a`](https://git.odit.services/lfk/frontend/commit/4c0886a5d9b91439967bc8f66b09a57177f967d0)
- Fixed styling [`865254d`](https://git.odit.services/lfk/frontend/commit/865254d646b5f7de15720551c67ae649601cbcd2)
- Changed top info style for donation detail [`000fc97`](https://git.odit.services/lfk/frontend/commit/000fc97beb14427f69d421ff2c96975dbbdc7a3a)
#### [0.14.0](https://git.odit.services/lfk/frontend/compare/0.13.1...0.14.0)
> 14 April 2021
- Merge pull request 'added donor receipt list download to DonorsOverview' (#134) from feature/132-export-donors-receipt-list into dev [`#132`](https://git.odit.services/lfk/frontend/issues/132)
- Sorted translations 🌎 [`c6c9751`](https://git.odit.services/lfk/frontend/commit/c6c97516b3981ef580d620c0c8a6fcc42f26facd)
- Fixed typos in translations [`03676b2`](https://git.odit.services/lfk/frontend/commit/03676b2894892c3559118b93e969c063b53b081e)
- added donor receipt list download to DonorsOverview [`d241ca5`](https://git.odit.services/lfk/frontend/commit/d241ca569838abbe9581fbd319f7f3b563cb7dcc)
- 🚀RELEASE v0.14.0 [`9c5fc6b`](https://git.odit.services/lfk/frontend/commit/9c5fc6b61c0bb2a6d831d4a23ef8679c6e68c6a1)
- ⏫ general version bump [`18f151c`](https://git.odit.services/lfk/frontend/commit/18f151c1fb878a74c3d1a2c2a2debf7913739417)
- new license file version [CI SKIP] [`302caf0`](https://git.odit.services/lfk/frontend/commit/302caf015f88f77e2b2ae2b67680e79f987ad81e)
- Switched to selfhosted images [`112eb29`](https://git.odit.services/lfk/frontend/commit/112eb29f932cd936f1d6c2308dcaeaf8cb642490)
- ⏫ bump @odit/lfk-client-js@0.11.0 [`9ca57fa`](https://git.odit.services/lfk/frontend/commit/9ca57fac2eeabbf25142a507fb9c0fa3c90b4e74)
- replace donationAmount with paidDonationAmount [`e90e56d`](https://git.odit.services/lfk/frontend/commit/e90e56d8b26aef23aba2bbb0c3942ba4d7feb224)
#### [0.13.1](https://git.odit.services/lfk/frontend/compare/0.13.0...0.13.1)
> 11 April 2021
- 🚀RELEASE v0.13.1 [`b512cf8`](https://git.odit.services/lfk/frontend/commit/b512cf86674f1c60b5ac790985ededdfd6554185)
- For await fix [`a24d292`](https://git.odit.services/lfk/frontend/commit/a24d2923c6e6da90d610c05183d29d47eaf2ed30)
#### [0.13.0](https://git.odit.services/lfk/frontend/compare/0.12.5...0.13.0)
> 11 April 2021
- 🚀RELEASE v0.13.0 [`467808a`](https://git.odit.services/lfk/frontend/commit/467808abefe127dac66a2837fcce3197dddb140f)
- Merge pull request 'Better org pdf generation feature/130-org_doc_splitting' (#131) from feature/130-org_doc_splitting into dev [`861f1f2`](https://git.odit.services/lfk/frontend/commit/861f1f221653283e7586aa2c67b205337fd44398)
- Org card generation now runs in sequence [`fef14b6`](https://git.odit.services/lfk/frontend/commit/fef14b6e4fb47ad92da61de91fedce96aea26b2c)
- Org certificate generation now runs in sequence [`509b22b`](https://git.odit.services/lfk/frontend/commit/509b22bea0dd3e4446e6ecc37d27644e9bf2ad50)
- Org contract generation now runs in sequence [`01d2a7e`](https://git.odit.services/lfk/frontend/commit/01d2a7e6aa709b3f2d71575f705fc962e97e2742)
- Emergency document server url change [`5476808`](https://git.odit.services/lfk/frontend/commit/5476808683a919bc34dbaea1f1ed276d49750096)
- Fixed const -&gt; let [`7447b2f`](https://git.odit.services/lfk/frontend/commit/7447b2f4c134a585905db6733093eab13e6f7c47)
- Hotfix: Org * generation🐞 [`ac586fe`](https://git.odit.services/lfk/frontend/commit/ac586fec5abd324d590ba99cdfe8ddddefbf95e6)
#### [0.12.5](https://git.odit.services/lfk/frontend/compare/0.12.4...0.12.5)
> 8 April 2021
- 🚀RELEASE v0.12.5 [`331d737`](https://git.odit.services/lfk/frontend/commit/331d737796c82454b1c19fa1840ccc20e36d2626)
- Merge pull request 'Added runner team's parentorg name to runenr overciew' (#129) from feature/128-runner_orgs into dev [`ef81b8a`](https://git.odit.services/lfk/frontend/commit/ef81b8adf9bef685a55936d7544bf645c0d6ecbe)
- Switched to html entity [`8a7d635`](https://git.odit.services/lfk/frontend/commit/8a7d635cef2d465e70c84e1f7a7b90b98a8dbab1)
- Added runner team's parentorg name to runenr overciew [`4c259c1`](https://git.odit.services/lfk/frontend/commit/4c259c1eef2b0166ce6a8493d0c9e9d5ede11146)
#### [0.12.4](https://git.odit.services/lfk/frontend/compare/0.12.3...0.12.4) #### [0.12.4](https://git.odit.services/lfk/frontend/compare/0.12.3...0.12.4)
> 8 April 2021
- 🚀RELEASE v0.12.4 [`5b4ede5`](https://git.odit.services/lfk/frontend/commit/5b4ede5e2f6a26b475a7a4b430a4146d21fb9671)
- 🚑 [HOTFIX] - drop "svelte-infinite-loading" [`d0ab3dd`](https://git.odit.services/lfk/frontend/commit/d0ab3dda78bbad2cea18a2491056530897d56607) - 🚑 [HOTFIX] - drop "svelte-infinite-loading" [`d0ab3dd`](https://git.odit.services/lfk/frontend/commit/d0ab3dda78bbad2cea18a2491056530897d56607)
#### [0.12.3](https://git.odit.services/lfk/frontend/compare/0.12.2...0.12.3) #### [0.12.3](https://git.odit.services/lfk/frontend/compare/0.12.2...0.12.3)

View File

@@ -1,12 +1,14 @@
FROM registry.odit.services/hub/library/node:19.5.0-alpine3.16 as build FROM node:15.5.1-alpine3.12
WORKDIR /app WORKDIR /app
COPY package.json ./ COPY package.json ./
RUN npx pnpm i RUN yarn
COPY package.json *.config.js postcss.config.cjs tailwind.config.js vite.config.js index.html ./ COPY package.json *.config.js index.html ./
COPY src ./src COPY src ./src
COPY public ./public COPY public ./public
RUN yarn build RUN yarn build
# final image # final image
FROM registry.odit.services/library/nginx-brotli:3.15 as final FROM alpine
COPY --from=build /app/dist /usr/share/nginx/html COPY --from=0 /app/dist /app
FROM fholzer/nginx-brotli:v1.19.1
COPY --from=1 /app /usr/share/nginx/html
COPY ./nginx.conf /etc/nginx/nginx.conf COPY ./nginx.conf /etc/nginx/nginx.conf

View File

@@ -13,7 +13,7 @@
</head> </head>
<body> <body>
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.16.3-RELEASE_INFO</span> <span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.12.4-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>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>

View File

@@ -6,11 +6,6 @@ http {
server { server {
error_page 404 /index.html; error_page 404 /index.html;
root /usr/share/nginx/html; root /usr/share/nginx/html;
location /assets {
expires 1y;
log_not_found off;
access_log off;
}
location = /index.html { location = /index.html {
add_header Cache-Control 'no-store'; add_header Cache-Control 'no-store';
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-frontend", "name": "@odit/lfk-frontend",
"version": "0.16.3", "version": "0.12.4",
"scripts": { "scripts": {
"i18n-order": "node order.js", "i18n-order": "node order.js",
"dev": "vite", "dev": "vite",
@@ -10,31 +10,30 @@
}, },
"license": "CC-BY-NC-SA-4.0", "license": "CC-BY-NC-SA-4.0",
"devDependencies": { "devDependencies": {
"@odit/lfk-client-js": "0.13.1", "check-password-strength": "2.0.2",
"@odit/lfk-client-js": "0.10.1",
"@odit/license-exporter": "0.0.11", "@odit/license-exporter": "0.0.11",
"@sveltejs/vite-plugin-svelte": "1.0.0-next.6", "@sveltejs/vite-plugin-svelte": "1.0.0-next.6",
"@types/html-minifier": "4.0.0", "@types/html-minifier": "4.0.0",
"@vincjo/datatables": "^1.1.0",
"auto-changelog": "2.2.1", "auto-changelog": "2.2.1",
"autoprefixer": "10.2.5", "autoprefixer": "10.2.5",
"check-password-strength": "2.0.2",
"csvtojson": "2.0.10", "csvtojson": "2.0.10",
"gridjs": "3.4.0", "gridjs": "3.4.0",
"html-minifier": "4.0.0", "html-minifier": "4.0.0",
"localforage": "1.9.0", "localforage": "1.9.0",
"marked": "2.0.3", "marked": "2.0.1",
"postcss": "8.2.10", "release-it": "14.5.1",
"release-it": "14.6.1",
"svelte": "3.37.0", "svelte": "3.37.0",
"svelte-focus-trap": "1.2.0", "svelte-focus-trap": "1.2.0",
"svelte-i18n": "3.3.9", "svelte-i18n": "3.3.9",
"svelte-preprocess": "4.7.0", "svelte-preprocess": "4.7.0",
"svelte-select": "3.17.0", "svelte-select": "3.17.0",
"tailwindcss": "3.2.7", "tailwindcss": "2.1.1",
"tinro": "0.6.1", "tinro": "0.6.1",
"toastify-js": "1.10.0", "toastify-js": "1.10.0",
"validator": "13.5.2", "validator": "13.5.2",
"vite": "2.1.5", "vite": "2.1.5",
"vite-plugin-windicss": "0.12.5",
"xlsx": "0.16.9" "xlsx": "0.16.9"
}, },
"release-it": { "release-it": {
@@ -53,8 +52,5 @@
"hooks": { "hooks": {
"after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js && git add index.html && node order.js && git add src/locales" "after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js && git add index.html && node order.js && git add src/locales"
} }
},
"dependencies": {
"@paralleldrive/cuid2": "^2.2.0"
} }
} }

View File

@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};

View File

@@ -1,6 +1,5 @@
const config = { const config = {
baseurl: 'http://localhost:4010', baseurl: 'http://localhost:4010',
baseurl_documentserver: 'http://localhost:4010/documents',
documentserver_key: 'NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe', documentserver_key: 'NqZSYTy5AFQ7MppbLW5moqpTk7u7YrNUHKYhKYuThnnya2WpCOIU694hIZT1FzYe',
// optional // optional
default_username: 'demo', default_username: 'demo',

File diff suppressed because one or more lines are too long

View File

@@ -72,8 +72,6 @@
import Scans from "./components/scans/Scans.svelte"; import Scans from "./components/scans/Scans.svelte";
import ScanDetail from "./components/scans/ScanDetail.svelte"; import ScanDetail from "./components/scans/ScanDetail.svelte";
import Cards from "./components/cards/Cards.svelte"; import Cards from "./components/cards/Cards.svelte";
import StatsClients from "./components/statsclients/StatsClients.svelte";
import StatsClientDetail from "./components/statsclients/StatsClientDetail.svelte";
store.init(); store.init();
</script> </script>
@@ -208,14 +206,6 @@
<ScanStationDetail {params} /> <ScanStationDetail {params} />
</Route> </Route>
</Route> </Route>
<Route path="/statsclients/*">
<Route path="/">
<StatsClients />
</Route>
<Route path="/:clientid" let:params>
<StatsClientDetail {params} />
</Route>
</Route>
<Route path="/about"> <Route path="/about">
<About /> <About />
</Route> </Route>

View File

@@ -76,7 +76,7 @@
// last login was not processed yet // last login was not processed yet
} else { } else {
Toastify({ Toastify({
text: $_('please-wait-a-moment-your-login-is-still-being-processed'), text: "chill...",
duration: 1500, duration: 1500,
backgroundColor: backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", "linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",

View File

@@ -1,6 +0,0 @@
<!--
Temporary tailwind import fixes for classes that wouldn't be directly used otherwise.
Or as others may call it: Real big bullshit time.
Issue: https://git.odit.services/lfk/frontend/issues/136
-->
<div class="opacity-50"></div>

View File

@@ -77,7 +77,7 @@
duration: -1, duration: -1,
}).showToast(); }).showToast();
fetch( fetch(
`${config.baseurl_documentserver}/cards?&download=true&key=${config.documentserver_key}`, `${config.baseurl}/documents/cards?&download=true&key=${config.documentserver_key}`,
{ {
method: "POST", method: "POST",
headers: { headers: {

View File

@@ -5,7 +5,6 @@
import { RunnerCardService, RunnerService } from "@odit/lfk-client-js"; import { RunnerCardService, RunnerService } from "@odit/lfk-client-js";
import Select from "svelte-select"; import Select from "svelte-select";
import Toastify from "toastify-js"; import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte";
export let edit_modal_open; export let edit_modal_open;
export let current_cards; export let current_cards;
export let runner = {}; export let runner = {};
@@ -22,10 +21,6 @@
$: runners = []; $: runners = [];
$: enabled = true; $: enabled = true;
$: processed_last_submit = true; $: processed_last_submit = true;
const dispatch = createEventDispatcher();
function dataUpdated() {
dispatch('dataUpdated',);
}
RunnerService.runnerControllerGetAll().then((val) => { RunnerService.runnerControllerGetAll().then((val) => {
runners = val.map((r) => { runners = val.map((r) => {
return { label: getRunnerLabel(r), value: r }; return { label: getRunnerLabel(r), value: r };
@@ -70,7 +65,6 @@
}).showToast(); }).showToast();
current_cards[current_cards.findIndex((c) => c.id === id)] = result; current_cards[current_cards.findIndex((c) => c.id === id)] = result;
current_cards = current_cards; current_cards = current_cards;
dataUpdated();
}) })
.catch((err) => { .catch((err) => {
// //

View File

@@ -3,230 +3,279 @@
import { RunnerCardService } from "@odit/lfk-client-js"; import { RunnerCardService } from "@odit/lfk-client-js";
import store from "../../store"; import store from "../../store";
import Toastify from "toastify-js"; import Toastify from "toastify-js";
import { DataHandler, Datatable, Th, ThFilter } from "@vincjo/datatables";
import CardsEmptyState from "./CardsEmptyState.svelte"; import CardsEmptyState from "./CardsEmptyState.svelte";
import CardDetailModal from "./CardDetailModal.svelte"; import CardDetailModal from "./CardDetailModal.svelte";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import ThFilterStatus from "./ThFilterStatus.svelte";
export let edit_modal_open = false; export let edit_modal_open = false;
export let runner = {}; export let runner = {};
export let editable = {}; export let editable = {};
export let original_data = {}; export let original_data = {};
export let current_cards = []; export let current_cards = [];
const handler = new DataHandler(current_cards, { rowsPerPage: 50 }); $: filtered_cards = current_cards.filter(function (c) {
const rows = handler.getRows(); if (
c.code.toLowerCase().includes(searchvalue_lowercase) ||
c.runner?.firstname.toLowerCase().includes(searchvalue_lowercase) ||
c.runner?.middlename.toLowerCase().includes(searchvalue_lowercase) ||
c.runner?.lastname.toLowerCase().includes(searchvalue_lowercase) ||
should_display_based_on_id(c.id)
) {
return true;
}
});
$: searchvalue = "";
$: searchvalue_lowercase = searchvalue.toLowerCase();
$: active_deletes = []; $: active_deletes = [];
$: cards_show = generate_cards.length > 0; $: cards_show = current_cards.some((r) => r.is_selected === true);
$: generate_cards = []; $: generate_cards = current_cards.filter((r) => r.is_selected === true);
const cards_promise = RunnerCardService.runnerCardControllerGetAll().then( const cards_promise = RunnerCardService.runnerCardControllerGetAll().then(
(val) => { (val) => {
current_cards = val; current_cards = val;
handler.setRows(val);
} }
); );
function should_display_based_on_id(id) {
if (searchvalue.toString().slice(-1) === "*") {
return id.toString().startsWith(searchvalue.replace("*", ""));
}
return id.toString() === searchvalue;
}
const getRunnerLabel = (option) => const getRunnerLabel = (option) =>
option?.firstname + option?.firstname + " " + (option?.middlename || "") + " " + (option?.lastname || "{$_('non-blanko')}");
" " +
(option?.middlename || "") +
" " +
(option?.lastname || "{$_('non-blanko')}");
function open_edit_modal(card) { function open_edit_modal(card) {
if (card.runner?.id) { if(card.runner?.id){
runner = Object.assign( runner = Object.assign(
{ runner }, { runner },
{ label: getRunnerLabel(card.runner), value: card.runner } { label: getRunnerLabel(card.runner), value: card.runner }
); );
card.runner = card.runner.id; card.runner = card.runner.id;
} else { }
card.runner = null; else{
runner = null; card.runner=null;
runner = null
} }
editable = Object.assign(editable, card); editable = Object.assign(editable, card);
original_data = Object.assign(original_data, card); original_data = Object.assign(original_data, card);
edit_modal_open = true; edit_modal_open = true;
} }
// -----------------
let scrollTop = 0;
$: rendered = filtered_cards;
let innerHeight = 0;
let ele;
$: updateSlice(scrollTop);
$: innerHeight = `${filtered_cards.length * 25}px`;
$: if (ele) updateSlice();
function updateSlice() {
const height = ele ? parseInt(ele.clientHeight) : 100;
const init = scrollTop / 25;
const end = Math.ceil((scrollTop + height) / 25);
rendered = filtered_cards.slice(init, end + 15);
}
function updateScroll($event) {
scrollTop = $event.target.scrollTop;
}
</script> </script>
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:UPDATE")} <style>
table tbody {
display: block;
overflow-y: scroll;
}
table thead, table tbody tr {
display: table;
width: 100%;
table-layout: fixed;
}
</style>
{#if store.state.jwtinfo.userdetails.permissions.includes('CARD:UPDATE')}
<CardDetailModal <CardDetailModal
bind:current_cards bind:current_cards
bind:edit_modal_open bind:edit_modal_open
bind:runner bind:runner
bind:editable bind:editable
bind:original_data bind:original_data />
on:dataUpdated={(handler.setRows(current_cards))}
/>
{/if} {/if}
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes('CARD:GET')}
{#await cards_promise} {#await cards_promise}
<div <div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
role="alert" role="alert">
> <p class="font-bold">{$_('loading-cards')}</p>
<p class="font-bold">{$_("loading-cards")}</p> <p class="text-sm">{$_('this-might-take-a-moment')}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div> </div>
{:then} {:then}
{#if current_cards.length === 0} {#if current_cards.length === 0}
<CardsEmptyState /> <CardsEmptyState />
{:else} {:else}
<div class="h-12"> <input
<GenerateRunnerCards bind:cards_show bind:generate_cards /> type="search"
</div> bind:value={searchvalue}
<Datatable {handler}> placeholder={$_('datatable.search')}
<table> aria-label={$_('datatable.search')}
<thead> class="gridjs-input gridjs-search-input mb-4" />
<div class="h-12">
<GenerateRunnerCards
bind:cards_show
bind:generate_cards />
</div>
<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> <tr>
<th> <th
<input scope="col"
type="checkbox" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" <span
checked={generate_cards.length == current_cards.length}
on:click={() => { on:click={() => {
if (generate_cards.length != current_cards.length) { const newstate = !current_cards.some((r) => r.is_selected === true);
generate_cards = current_cards; current_cards = current_cards.map((r) => {
} else { r.is_selected = newstate;
generate_cards = []; return r;
} });
}} }}
/> class="underline cursor-pointer select-none">{#if current_cards.some((r) => r.is_selected === true)}
{$_('deselect-all')}
{:else}{$_('select-all')}{/if}
</span>
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('code')}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('runner')}
</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> </th>
<Th {handler} orderBy="code">{$_("code")}</Th>
<Th {handler} orderBy="runner">{$_("runner")}</Th>
<Th {handler} orderBy="status">{$_("status")}</Th>
<th>{$_("action")}</th>
</tr>
<tr>
<th />
<ThFilter {handler} filterBy="code" />
<ThFilter {handler} filterBy="runner" />
<ThFilterStatus {handler} />
<th />
</tr> </tr>
</thead> </thead>
<tbody> <tbody class="divide-y divide-gray-200 virtual-wrapper"
{#each $rows as row} on:scroll={updateScroll}
<tr> style="height: 70vh; width:100%"
<td> bind:this={ele}
<input >
type="checkbox" {#each filtered_cards as card, index}
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" {#if card.code
checked={generate_cards.filter((i) => i.id == row.id) .toLowerCase()
.length > 0} .includes(
on:click={() => { searchvalue.toLowerCase()
if ( ) || card.runner?.firstname
generate_cards.findIndex((i) => i.id == row.id) == -1 .toLowerCase()
) { .includes(
generate_cards.push(row); searchvalue.toLowerCase()
generate_cards = generate_cards; ) || card.runner?.middlename
} else { .toLowerCase()
generate_cards = generate_cards.filter( .includes(
(r) => r.id != row.id searchvalue.toLowerCase()
); ) || card.runner?.lastname
} .toLowerCase()
console.log(generate_cards); .includes(
}} searchvalue.toLowerCase()
/> ) || should_display_based_on_id(card.id)}
</td> <tr data-rowid="card_{card.id}">
<td>{row.code}</td> <td class="px-6 py-4 whitespace-nowrap">
<td> <input
{#if row.runner} bind:checked={card.is_selected}
<a type="checkbox"
href="../runners/{row.runner.id}" class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800" </td>
>{row.runner.firstname} <td class="px-6 py-4 whitespace-nowrap">
{row.runner.middlename || ""} <div class="flex items-center">{card.code}</div>
{row.runner.lastname}</a </td>
> <td class="px-6 py-4 whitespace-nowrap">
{:else}{$_("non-blanko")}{/if} <div class="flex items-center">
</td> {#if card.runner}
<td> <a
{#if row.enabled} href="../runners/{card.runner.id}"
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{card.runner.firstname}
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800" {card.runner.middlename || ''}
>{$_("enabled")}</span {card.runner.lastname}</a>
> {:else}{$_('non-blanko')}{/if}
{:else} </div>
<span </td>
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800" <td class="px-6 py-4 whitespace-nowrap">
>{$_("disabled")}</span <div class="flex items-center">
> {#if card.enabled}
{/if} <span
</td> class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">{$_('enabled')}</span>
<td> {:else}
{#if active_deletes[row.id] === true} <span
<button class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">{$_('disabled')}</span>
on:click={() => { {/if}
active_deletes[row.id] = false; </div>
}} </td>
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer" {#if active_deletes[card.id] === true}
>{$_("cancel-delete")}</button <td
> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button <button
on:click={() => { on:click={() => {
RunnerCardService.runnerCardControllerRemove( active_deletes[card.id] = false;
row.id, }}
true tabindex="0"
) class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
.then((resp) => { <button
current_cards = current_cards.filter( on:click={() => {
(obj) => obj.id !== row.id RunnerCardService.runnerCardControllerRemove(card.id, false).then(
); (resp) => {
}) current_cards = current_cards.filter(
.catch((err) => {}); (obj) => obj.id !== card.id
}} );
tabindex="0" Toastify({
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer" text: $_('card-deleted'),
>{$_("confirm-delete")}</button duration: 500,
> backgroundColor:
{:else} 'linear-gradient(to right, #00b09b, #96c93d)',
<button }).showToast();
on:click={() => { }
open_edit_modal(row); );
}} }}
class="text-indigo-600 hover:text-indigo-900" tabindex="0"
>{$_("details")}</button class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('confirm-delete')}</button>
> </td>
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:DELETE")} {:else}
<button <td
on:click={() => { class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
active_deletes[row.id] = true; <button
}} on:click={() => {
tabindex="0" open_edit_modal(card);
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer" }}
>{$_("delete")}</button class="text-indigo-600 hover:text-indigo-900">{$_('details')}</button>
> {#if store.state.jwtinfo.userdetails.permissions.includes('CARD:DELETE')}
{/if} <button
{/if} on:click={() => {
</td> active_deletes[card.id] = true;
</tr> }}
{/each} tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button>
{/if}
</td>
{/if}
</tr>
{/if}
{/each}
</tbody> </tbody>
</table> </table>
</Datatable> </div>
{/if} {/if}
{:catch error} {:catch error}
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500"> <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"> <span class="inline-block align-middle mr-8">
<b class="capitalize">{$_("general_promise_error")}</b> <b class="capitalize">{$_('general_promise_error')}</b>
{error} {error}
</span> </span>
</div> </div>
{/await} {/await}
{/if} {/if}
<style>
table tbody {
display: block;
overflow-y: scroll;
}
table thead,
table tbody tr {
display: table;
width: 100%;
table-layout: fixed;
}
</style>

View File

@@ -1,29 +0,0 @@
<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>

View File

@@ -86,7 +86,7 @@
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ const toast = Toastify({
text: $_('contact-is-being-added'), text: "Contact is being added...",
duration: -1, duration: -1,
}).showToast(); }).showToast();
let address = {}; let address = {};
@@ -123,7 +123,7 @@
modal_open = false; modal_open = false;
// //
Toastify({ Toastify({
text: $_('contact-added'), text: "Contact added",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();

View File

@@ -256,26 +256,6 @@
<span>{$_('scanstations')}</span> <span>{$_('scanstations')}</span>
</a> </a>
{/if} {/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:GET')}
<a
class:bg-gray-100={$router.path === '/statsclients/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/statsclients/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
width="24"
height="24"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"><path
fill="none"
d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg>
<span>{$_('statsclients')}</span>
</a>
{/if}
<a <a
class:bg-gray-100={$router.path === '/settings/'} class:bg-gray-100={$router.path === '/settings/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900" class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"

View File

@@ -1,157 +1,22 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { StatsService } from "@odit/lfk-client-js"; import StatCards from "./StatCards.svelte";
import StatCards from "./StatCard.svelte";
import store from "../../store"; import store from "../../store";
import StatCard from "./StatCard.svelte";
let navOpen = false; let navOpen = false;
const stats_promise = StatsService.statsControllerGet();
</script> </script>
<div <div
class="p-5 overflow-x-hidden" class="p-5 overflow-x-hidden"
on:click={() => { on:click={() => {
navOpen = false; navOpen = false;
}} }}>
>
<h1 class="text-3xl leading-tight"> <h1 class="text-3xl leading-tight">
<span class="font-extrabold">{$_("dashboard-title")}</span> <span class="font-extrabold">{$_('dashboard-title')}</span>
<span> <span>
- -
{$_("dashboard-greeting")}, {$_('dashboard-greeting')},
<span class="text-blue-500" <span
>{store.state.jwtinfo.userdetails.firstname} class="text-blue-500">{store.state.jwtinfo.userdetails.firstname} {store.state.jwtinfo.userdetails.lastname}</span></span>
{store.state.jwtinfo.userdetails.lastname}</span
></span
>
</h1> </h1>
<h1>{$_("general-stats")}</h1> <StatCards />
{#await stats_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">{$_("stats-are-being-loaded")}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div>
{:then stats}
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5 2xl:grid-cols-6 gap-4">
<StatCard
title={$_("runners")}
value={stats.total_runners}
href="/runners/"
>
<svg
height="24"
width="24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path d="M0 0h24v24H0z" fill="none" />
<path
d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z"
/></svg
>
</StatCard>
<StatCard
title={$_("total-scans")}
value={stats.total_scans}
href="/scans/"
>
<svg
stroke="currentColor"
fill="currentColor"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
size="24"
class="stroke-current text-grey-500"
height="24"
width="24"
xmlns="http://www.w3.org/2000/svg"
><polyline points="22 12 18 12 15 21 9 3 6 12 2 12" /></svg
>
</StatCard>
<StatCard
title={$_("total-donations")}
value={`${(stats.total_donation / 100).toFixed(2)} €`}
href="/donations/"
>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
fill="currentColor"
width="24"
><path d="M0 0h24v24H0z" fill="none" />
<path
d="M15 18.5A6.48 6.48 0 019.24 15H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24A6.491 6.491 0 0115 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.955 8.955 0 0015 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06a8.262 8.262 0 000 2H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z"
/></svg
>
</StatCard>
<StatCard
title={$_("total-distance")}
value={`${stats.total_distance / 1000} km`}
href="#"
>
<svg
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
height="24"
width="24"
><path d="M0 0h24v24H0z" fill="none" />
<path
d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z"
/></svg
>
</StatCard>
<StatCard
title={$_("count_teams")}
value={stats.total_teams}
href="/teams/"
>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
size="24"
class="stroke-current text-grey-500"
height="24"
width="24"
xmlns="http://www.w3.org/2000/svg"
><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
<path d="M16 3.13a4 4 0 0 1 0 7.75" /></svg
>
</StatCard>
<StatCard
title={$_("count_organizations")}
value={stats.total_orgs}
href="/orgs/"
>
<svg
height="24"
fill="currentColor"
width="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
><path fill="none" d="M0 0h24v24H0z" />
<path
d="M17 11V3H7v4H3v14h8v-4h2v4h8V11h-4zM7 19H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm4 4H9v-2h2v2zm0-4H9V9h2v2zm0-4H9V5h2v2zm4 8h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm4 12h-2v-2h2v2zm0-4h-2v-2h2v2z"
/></svg
>
</StatCard>
</div>
{: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}
</div> </div>

View File

@@ -1,22 +0,0 @@
<script>
import { _ } from "svelte-i18n";
export let href = "#"
export let title = "";
export let value = "";
</script>
<a href={href}>
<div
class="p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-col">
<div class="text-xs uppercase font-light text-grey-500">
{title}
</div>
<div class="text-xl font-bold">{value}</div>
</div>
<slot></slot>
</div>
</div>
</a>

View File

@@ -0,0 +1,165 @@
<script>
import { StatsService } from "@odit/lfk-client-js";
import { _ } from "svelte-i18n";
const stats_promise = StatsService.statsControllerGet();
</script>
<!-- -->
<h1>{$_('general-stats')}</h1>
{#await stats_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">{$_('stats-are-being-loaded')}</p>
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
</div>
{:then stats}
<div
class="flex flex-col lg:flex-row w-full lg:space-x-2 space-y-2 lg:space-y-0 mb-2 lg:mb-4">
<a href="/runners/" class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-col">
<div class="text-xs uppercase font-light text-grey-500">
{$_('runners')}
</div>
<div class="text-xl font-bold">{stats.total_runners}</div>
</div>
<svg
height="24"
width="24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none" />
<path
d="M13.49 5.48c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm-3.6 13.9l1-4.4 2.1 2v6h2v-7.5l-2.1-2 .6-3c1.3 1.5 3.3 2.5 5.5 2.5v-2c-1.9 0-3.5-1-4.3-2.4l-1-1.6c-.4-.6-1-1-1.7-1-.3 0-.5.1-.8.1l-5.2 2.2v4.7h2v-3.4l1.8-.7-1.6 8.1-4.9-1-.4 2 7 1.4z" /></svg>
</div>
</div>
</a>
<div class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-col">
<div class="text-xs uppercase font-light text-grey-500">
{$_('total-scans')}
</div>
<div class="text-xl font-bold">{stats.total_scans}</div>
</div><svg
stroke="currentColor"
fill="currentColor"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
size="24"
class="stroke-current text-grey-500"
height="24"
width="24"
xmlns="http://www.w3.org/2000/svg"><polyline
points="22 12 18 12 15 21 9 3 6 12 2 12" /></svg>
</div>
</div>
</div>
<div class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-col">
<div class="text-xs uppercase font-light text-grey-500">
{$_('total-donations')}
</div>
<div class="text-xl font-bold">{stats.total_donation}</div>
</div><svg
xmlns="http://www.w3.org/2000/svg"
height="24"
fill="currentColor"
width="24"><path d="M0 0h24v24H0z" fill="none" />
<path
d="M15 18.5A6.48 6.48 0 019.24 15H15v-2H8.58c-.05-.33-.08-.66-.08-1s.03-.67.08-1H15V9H9.24A6.491 6.491 0 0115 5.5c1.61 0 3.09.59 4.23 1.57L21 5.3A8.955 8.955 0 0015 3c-3.92 0-7.24 2.51-8.48 6H3v2h3.06a8.262 8.262 0 000 2H3v2h3.52c1.24 3.49 4.56 6 8.48 6 2.31 0 4.41-.87 6-2.3l-1.78-1.77c-1.13.98-2.6 1.57-4.22 1.57z" /></svg>
</div>
</div>
</div>
<div class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-col">
<div class="text-xs uppercase font-light text-grey-500">
{$_('total-distance')}
</div>
<div class="text-xl font-bold">
{stats.total_distance / 1000}
km
</div>
</div>
<svg
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
height="24"
width="24"><path d="M0 0h24v24H0z" fill="none" />
<path
d="M21 6H3c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 10H3V8h2v4h2V8h2v4h2V8h2v4h2V8h2v4h2V8h2v8z" /></svg>
</div>
</div>
</div>
<a href="/teams/" class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-col">
<div class="text-xs uppercase font-light text-grey-500">
{$_('count_teams')}
</div>
<div class="text-xl font-bold">{stats.total_teams}</div>
</div>
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
size="24"
class="stroke-current text-grey-500"
height="24"
width="24"
xmlns="http://www.w3.org/2000/svg"><path
d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
<circle cx="9" cy="7" r="4" />
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
<path d="M16 3.13a4 4 0 0 1 0 7.75" /></svg>
</div>
</div>
</a>
<a href="/orgs/" class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between">
<div class="flex flex-col">
<div class="text-xs uppercase font-light text-grey-500">
{$_('count_organizations')}
</div>
<div class="text-xl font-bold">{stats.total_orgs}</div>
</div>
<svg
height="24"
fill="currentColor"
width="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" />
<path
d="M17 11V3H7v4H3v14h8v-4h2v4h8V11h-4zM7 19H5v-2h2v2zm0-4H5v-2h2v2zm0-4H5V9h2v2zm4 4H9v-2h2v2zm0-4H9V9h2v2zm0-4H9V5h2v2zm4 8h-2v-2h2v2zm0-4h-2V9h2v2zm0-4h-2V5h2v2zm4 12h-2v-2h2v2zm0-4h-2v-2h2v2z" /></svg>
</div>
</div>
</a>
</div>
{: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}

View File

@@ -9,7 +9,6 @@
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Select from "svelte-select"; import Select from "svelte-select";
import Toastify from "toastify-js"; import Toastify from "toastify-js";
import { is_promise } from "svelte/internal";
export let modal_open; export let modal_open;
export let current_donations; export let current_donations;
const getDonorLabel = (option) => const getDonorLabel = (option) =>
@@ -25,7 +24,6 @@ import { is_promise } from "svelte/internal";
$: donors = []; $: donors = [];
$: runners = []; $: runners = [];
$: is_fixed = false; $: is_fixed = false;
$: is_paid = false;
DonorService.donorControllerGetAll().then((val) => { DonorService.donorControllerGetAll().then((val) => {
donors = val.map((r) => { donors = val.map((r) => {
return { label: getDonorLabel(r), value: r }; return { label: getDonorLabel(r), value: r };
@@ -59,18 +57,14 @@ import { is_promise } from "svelte/internal";
let amount_cent = Math.floor(amount_input * 100); let amount_cent = Math.floor(amount_input * 100);
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ const toast = Toastify({
text: $_('adding-donation'), text: "adding donation",
duration: -1, duration: -1,
}).showToast(); }).showToast();
if (is_fixed) { if (is_fixed) {
let postdata = { let postdata = {
donor, donor,
amount: amount_cent, amount: amount_cent,
paidAmount: 0
}; };
if(is_paid){
postdata.paidAmount = amount_cent;
}
DonationService.donationControllerPostFixed(postdata) DonationService.donationControllerPostFixed(postdata)
.then((result) => { .then((result) => {
donor = donors[0].id || 0; donor = donors[0].id || 0;
@@ -79,7 +73,7 @@ import { is_promise } from "svelte/internal";
modal_open = false; modal_open = false;
// //
Toastify({ Toastify({
text: $_('donation_added'), text: "donation_added",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
@@ -108,7 +102,7 @@ import { is_promise } from "svelte/internal";
modal_open = false; modal_open = false;
// //
Toastify({ Toastify({
text: $_('donation_added'), text: "donation_added",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
@@ -129,7 +123,7 @@ import { is_promise } from "svelte/internal";
</script> </script>
<style> <style>
.toggle:before { input:before {
content: ""; content: "";
position: absolute; position: absolute;
width: 1.25rem; width: 1.25rem;
@@ -143,12 +137,12 @@ import { is_promise } from "svelte/internal";
transition: 0.2s ease-in-out; transition: 0.2s ease-in-out;
} }
.toggle:checked { input:checked {
/* @apply: bg-indigo-400; */ /* @apply: bg-indigo-400; */
background-color: #7f9cf5; background-color: #7f9cf5;
} }
.toggle:checked:before { input:checked:before {
left: 1.25rem; left: 1.25rem;
} }
</style> </style>
@@ -201,7 +195,7 @@ import { is_promise } from "svelte/internal";
class="ml-2 text-base" class="ml-2 text-base"
class:text-gray-300={is_fixed}>{$_('distance-donation')}</span> class:text-gray-300={is_fixed}>{$_('distance-donation')}</span>
<input <input
class="toggle relative w-10 h-5 transition-all duration-200 ease-in-out bg-gray-400 rounded-full shadow-inner outline-none appearance-none align-middle" class="relative w-10 h-5 transition-all duration-200 ease-in-out bg-gray-400 rounded-full shadow-inner outline-none appearance-none align-middle"
type="checkbox" type="checkbox"
bind:checked={is_fixed} /> bind:checked={is_fixed} />
<span <span
@@ -273,29 +267,6 @@ import { is_promise } from "svelte/internal";
</span> </span>
{/if} {/if}
</div> </div>
{#if is_fixed}
<div class="col-span-6">
<label
for="paid"
class="block text-sm font-medium text-gray-700">{$_('already-paid')}</label>
<p class="text-gray-500">
<input
id="paid"
bind:checked={is_paid}
name="paid"
type="checkbox"
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" >
<span class="align-text-bottom">
{#if is_paid}
{$_('paid')}
{:else}
{$_('open')}
{/if}
</span>
</p>
</div>
{/if}
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,202 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick";
import { focusTrap } from "svelte-focus-trap";
import { DonationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
export let payment_modal_open = false;
export let current_donations = [];
export let editable = {};
export let original_data = {};
export let paid_amount_input = 0;
$:processed_last_submit=true;
function focus(el) {
el.focus();
}
$: createbtnenabled = is_paid_amount_valid && !(paid_amount_input*100 == original_data.paidAmount)
$: is_paid_amount_valid = paid_amount_input > 0;
(() => {
document.onkeydown = (e) => {
e = e || window.event;
if (e.key === "Escape") {
payment_modal_open = false;
}
if (e.keyCode === 13) {
if (createbtnenabled === true) {
createbtnenabled = false;
submit();
}
}
};
})();
function submit() {
if (processed_last_submit === true) {
processed_last_submit = false;
const toast = Toastify({
text: $_('updating-donation'),
duration: -1,
}).showToast();
editable.donor = editable.donor.id;
editable.paidAmount = paid_amount_input*100;
if(editable.responseType == "DISTANCEDONATION" || editable.runner){
editable.runner = editable.runner.id;
DonationService.donationControllerPutDistance(original_data.id, editable)
.then((result) => {
let id = original_data.id;
editable = {};
original_data = {};
payment_modal_open = false;
//
Toastify({
text: $_('donation-updated'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_donations[current_donations.findIndex((c) => c.id === id)] = result;
current_donations = current_donations;
})
.catch((err) => {
//
})
.finally(() => {
processed_last_submit = true;
//
toast.hideToast();
});
}
else{
DonationService.donationControllerPutFixed(original_data.id, editable)
.then((result) => {
let id = original_data.id;
editable = {};
original_data = {};
payment_modal_open = false;
//
Toastify({
text: $_('donation-updated'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_donations[current_donations.findIndex((c) => c.id === id)] = result;
current_donations = current_donations;
})
.catch((err) => {
//
})
.finally(() => {
processed_last_submit = true;
//
toast.hideToast();
});
}
}
}
</script>
{#if payment_modal_open}
<div
class="fixed z-10 inset-0 overflow-y-auto"
use:focusTrap
use:clickOutside
on:click_outside={() => {
payment_modal_open = false;
}}>
<div
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="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<div
class="inline-block align-bottom bg-white rounded-lg text-left shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<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="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
class="h-6 w-6 text-blue-600"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M22 10v10a1 1 0 01-1 1H3a1 1 0 01-1-1V10h20zm0-2H2V4a1 1 0 011-1h18a1 1 0 011 1v4zm-7 8v2h4v-2h-4z" /></svg>
</div>
<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">
{$_('enter-payment')}
</h3>
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_('you-can-enter-the-donations-paid-amount-manually-or-use-the-max-button-to-use-the-donations-exact-amount')}
</p>
</div>
<div class="grid grid-cols gap-6">
<div class="w-full">
<label
for="token"
class="block text-sm font-medium text-gray-700">{$_('paid-amount')}</label>
<div class="inline-flex border-gray-300 border rounded-l-md rounded-r-md bg-gray-50 text-gray-500 w-full">
<input
autocomplete="off"
class:border-red-500={!is_paid_amount_valid}
class:focus:border-red-500={!is_paid_amount_valid}
class:focus:ring-red-500={!is_paid_amount_valid}
bind:value={paid_amount_input}
type="number"
step="0.01"
name="donation_amount_eur"
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm p-2"
placeholder="2.00" />
<button
on:click={
()=>{
paid_amount_input=paid_amount_input = (original_data.amount/100).toFixed(2);
}
}
class="inline-flex items-center p-r-2 text-indigo-300 hover:text-indigo-700 text-sm">MAX</button>
<span
class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm"></span>
</div>
{#if !is_paid_amount_valid}
<span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
{$_('payment-amount-must-be-greater-than-0-00eur')}
</span>
{/if}
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
disabled={!createbtnenabled}
class:opacity-50={!createbtnenabled}
on:click={submit}
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">
{$_('save-changes')}
</button>
<button
on:click={() => {
payment_modal_open = false;
}}
type="button"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
{$_('cancel')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -20,8 +20,6 @@
$: current_runners = []; $: current_runners = [];
$: amount_input = 0; $: amount_input = 0;
$: is_amount_valid = amount_input > 0; $: is_amount_valid = amount_input > 0;
$: paid_amount_input = 0;
$: is_paid_amount_valid = paid_amount_input > 0;
$: is_everything_set = $: is_everything_set =
editable.donor != null && editable.donor != null &&
((original_data.responseType == "DISTANCEDONATION" && ((original_data.responseType == "DISTANCEDONATION" &&
@@ -32,17 +30,15 @@
(original_data.responseType == "DISTANCEDONATION" && (original_data.responseType == "DISTANCEDONATION" &&
!(Math.floor(amount_input * 100) === original_data.amountPerDistance)) || !(Math.floor(amount_input * 100) === original_data.amountPerDistance)) ||
(original_data.responseType !== "DISTANCEDONATION" && (original_data.responseType !== "DISTANCEDONATION" &&
!(Math.floor(amount_input * 100) === original_data.amount)) || !(Math.floor(amount_input * 100) === original_data.amount));
!(Math.floor(paid_amount_input * 100) === original_data.paidAmount);
$: save_enabled = changes_performed && is_amount_valid && is_everything_set; $: save_enabled = changes_performed && is_amount_valid && is_everything_set;
const promise = DonationService.donationControllerGetOne( const promise = DonationService.donationControllerGetOne(
params.donationid params.donationid
).then((data) => { ).then((data) => {
data_loaded = true; data_loaded = true;
original_data = Object.assign({}, data); original_data = Object.assign(original_data, data);
editable = Object.assign({}, original_data); editable = Object.assign(editable, original_data);
paid_amount_input = data.paidAmount / 100;
if (data.responseType == "DISTANCEDONATION") { if (data.responseType == "DISTANCEDONATION") {
amount_input = data.amountPerDistance / 100; amount_input = data.amountPerDistance / 100;
RunnerService.runnerControllerGetAll().then((val) => { RunnerService.runnerControllerGetAll().then((val) => {
@@ -70,11 +66,10 @@
function submit() { function submit() {
if (data_loaded === true && save_enabled) { if (data_loaded === true && save_enabled) {
Toastify({ Toastify({
text: $_('updating-donation'), text: "Donation is being updated",
duration: 2500, duration: 2500,
}).showToast(); }).showToast();
let postdata = {}; let postdata = {};
editable.paidAmount = paid_amount_input*100;
if (original_data.responseType === "DISTANCEDONATION") { if (original_data.responseType === "DISTANCEDONATION") {
editable.amountPerDistance = Math.floor(amount_input * 100); editable.amountPerDistance = Math.floor(amount_input * 100);
postdata = Object.assign(postdata, editable); postdata = Object.assign(postdata, editable);
@@ -88,7 +83,7 @@
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
Toastify({ Toastify({
text: $_('donation-updated'), text: "updated donation",
duration: 2500, duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
@@ -103,7 +98,7 @@
Object.assign(original_data, editable); Object.assign(original_data, editable);
original_data = original_data; original_data = original_data;
Toastify({ Toastify({
text: $_('donation-updated'), text: "updated donation",
duration: 2500, duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
@@ -117,7 +112,7 @@
DonationService.donationControllerRemove(original_data.id, false) DonationService.donationControllerRemove(original_data.id, false)
.then((resp) => { .then((resp) => {
Toastify({ Toastify({
text: $_('donation-deleted'), text: "Donation delete",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
@@ -224,24 +219,7 @@
<span>{(editable.amount / 100) <span>{(editable.amount / 100)
.toFixed(2) .toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })}</span> .toLocaleString('de-DE', { valute: 'EUR' })}</span>
|
<span
class="font-medium text-gray-700">{$_('paid-amount')}:</span>
<span>{(editable.paidAmount / 100)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })}</span>
|
<span
class="font-medium text-gray-700">{$_('status')}:</span>
{#if editable.status =="PAID"}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">{$_('paid')}</span>
{:else}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">{$_('open')}</span>
{/if}
</div> </div>
<br>
<div class=" w-full"> <div class=" w-full">
<label <label
for="donor" for="donor"
@@ -254,7 +232,7 @@
placeholder={$_('search-for-donor-name-or-id')} placeholder={$_('search-for-donor-name-or-id')}
noOptionsMessage={$_('no-donors-found')} noOptionsMessage={$_('no-donors-found')}
bind:selectedValue={donor} bind:selectedValue={donor}
on:select={(selectedValue) => {editable.donor = selectedValue.detail.value; editable.donor.donationAmount=original_data.donor.donationAmount; editable.donor.paidDonationAmount =original_data.donor.paidDonationAmount}} on:select={(selectedValue) => (editable.donor = selectedValue.detail.value)}
on:clear={() => (editable.donor = null)} /> on:clear={() => (editable.donor = null)} />
</div> </div>
{#if original_data.responseType == 'DISTANCEDONATION'} {#if original_data.responseType == 'DISTANCEDONATION'}
@@ -302,39 +280,6 @@
</span> </span>
{/if} {/if}
</div> </div>
<div class="w-full">
<label
for="token"
class="block text-sm font-medium text-gray-700">{$_('paid-amount')}</label>
<div class="inline-flex border-gray-300 border rounded-l-md rounded-r-md bg-gray-50 text-gray-500 w-full">
<input
autocomplete="off"
class:border-red-500={!is_amount_valid}
class:focus:border-red-500={!is_amount_valid}
class:focus:ring-red-500={!is_amount_valid}
bind:value={paid_amount_input}
type="number"
step="0.01"
name="donation_amount_eur"
class="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm p-2"
placeholder="2.00" />
<button
on:click={
()=>{
paid_amount_input=paid_amount_input = (original_data.amount/100).toFixed(2);
}
}
class="inline-flex items-center p-r-2 text-indigo-300 hover:text-indigo-700 text-sm">MAX</button>
<span
class="inline-flex items-center px-3 rounded-r-md border border-gray-300 bg-gray-50 text-gray-500 text-sm"></span>
</div>
{#if !is_paid_amount_valid}
<span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
{$_('payment-amount-must-be-greater-than-0-00eur')}
</span>
{/if}
</div>
</section> </section>
{:catch error} {:catch error}
<PromiseError {error} /> <PromiseError {error} />

View File

@@ -4,14 +4,9 @@
import store from "../../store"; import store from "../../store";
import Toastify from "toastify-js"; import Toastify from "toastify-js";
import DonationsEmptyState from "./DonationsEmptyState.svelte"; import DonationsEmptyState from "./DonationsEmptyState.svelte";
import AddDonationPaymentModal from "./AddDonationPaymentModal.svelte";
$: searchvalue = ""; $: searchvalue = "";
$: active_deletes = []; $: active_deletes = [];
export let current_donations = []; export let current_donations = [];
export let payment_modal_open = false;
export let editable = {};
export let original_data = {};
export let paid_amount_input = 0;
const donations_promise = DonationService.donationControllerGetAll().then( const donations_promise = DonationService.donationControllerGetAll().then(
(val) => { (val) => {
current_donations = val; current_donations = val;
@@ -23,15 +18,8 @@
} }
return id.toString() === searchvalue; return id.toString() === searchvalue;
} }
function open_payment_modal(donation) {
editable = Object.assign({}, donation);
original_data = Object.assign({}, donation);
paid_amount_input = (donation.paidAmount/100).toFixed(2);
payment_modal_open = true;
}
</script> </script>
<AddDonationPaymentModal bind:current_donations bind:original_data bind:editable bind:paid_amount_input bind:payment_modal_open />
{#if store.state.jwtinfo.userdetails.permissions.includes('DONATION:GET')} {#if store.state.jwtinfo.userdetails.permissions.includes('DONATION:GET')}
{#await donations_promise} {#await donations_promise}
<div <div
@@ -75,16 +63,6 @@
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('donation-amount')} {$_('donation-amount')}
</th> </th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('paid-amount')}
</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"> <th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_('action')}</span> <span class="sr-only">{$_('action')}</span>
</th> </th>
@@ -154,22 +132,6 @@
.toLocaleString('de-DE', { valute: 'EUR' })} .toLocaleString('de-DE', { valute: 'EUR' })}
</div> </div>
</td> </td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900">
{(donation.paidAmount / 100)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
{#if donation.status =="PAID"}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">{$_('paid')}</span>
{:else}
<span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">{$_('open')}</span>
{/if}
</td>
{#if active_deletes[donation.id] === true} {#if active_deletes[donation.id] === true}
<td <td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
@@ -187,7 +149,7 @@
(obj) => obj.id !== donation.id (obj) => obj.id !== donation.id
); );
Toastify({ Toastify({
text: $_('donation-deleted'), text: 'Donation deleted',
duration: 500, duration: 500,
backgroundColor: backgroundColor:
'linear-gradient(to right, #00b09b, #96c93d)', 'linear-gradient(to right, #00b09b, #96c93d)',
@@ -201,9 +163,6 @@
{:else} {:else}
<td <td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
on:click={() => {open_payment_modal(donation);}}
class="text-[#025a21] hover:text-green-900 mr-4">{$_('enter-payment')}</button>
<a <a
href="./{donation.id}" href="./{donation.id}"
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a> class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a>

View File

@@ -19,7 +19,7 @@
) )
.then((resp) => { .then((resp) => {
Toastify({ Toastify({
text: $_('donor-deleted'), text: "Donor deleted",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();

View File

@@ -193,12 +193,6 @@
<span>{(editable.donationAmount / 100) <span>{(editable.donationAmount / 100)
.toFixed(2) .toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })}</span> .toLocaleString('de-DE', { valute: 'EUR' })}</span>
|
<span
class="font-medium text-gray-700">{$_('total-paid-amount')}:</span>
<span>{(editable.paidDonationAmount / 100)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })}</span>
<br /> <br />
<span class="font-medium text-gray-700">{$_('donations')}:</span> <span class="font-medium text-gray-700">{$_('donations')}:</span>
{#if current_donations.filter((d) => d.donor.id == editable.id).length > 0} {#if current_donations.filter((d) => d.donor.id == editable.id).length > 0}
@@ -207,7 +201,7 @@
<a <a
href="../donations/{d.id}" href="../donations/{d.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-600 text-white mr-1">{d.runner.firstname} class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-600 text-white mr-1">{d.runner.firstname}
{d.runner.middlename || ""} {d.runner.middlename}
{d.runner.lastname}</a> {d.runner.lastname}</a>
{:else} {:else}
<a <a

View File

@@ -20,31 +20,6 @@
{$_('add-donor')} {$_('add-donor')}
</button> </button>
{/if} {/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('DONOR:GET')}
<button
on:click={() => {
const data = (current_donors.filter(d=>d.receiptNeeded===true)).map(function (d) {
d.address.address2=d.address.address2===""?"":" "+d.address.address2;
const address=`${d.address.address1}${d.address.address2}, ${d.address.postalcode} ${d.address.city}, ${d.address.country}`;
return [d.firstname,d.middlename,d.lastname,d.paidDonationAmount,address];
})
let csv = `${$_('csv_import__firstname')};${$_('csv_import__middlename')};${$_('csv_import__lastname')};${$_('total_donation_amount_in_eur')};${$_('address')}\n`;
data.forEach(function(row) {
csv += row.join(';');
csv += "\n";
});
let hiddenElement = document.createElement('a');
hiddenElement.href = 'data:text/csv;charset=utf-8,' + encodeURI(csv);
hiddenElement.target = '_blank';
hiddenElement.download = `${$_('filename_sponsoringquittungsliste')}.csv`;
hiddenElement.click();
hiddenElement.remove();
}}
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">
{$_('sponsoring-quittungs-liste_herunterladen')}
</button>
{/if}
</span> </span>
<DonorsOverview bind:current_donors /> <DonorsOverview bind:current_donors />
</section> </section>

View File

@@ -1,5 +1,5 @@
<script> <script>
import { _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import { DonationService, DonorService } from "@odit/lfk-client-js"; import { DonationService, DonorService } from "@odit/lfk-client-js";
import store from "../../store"; import store from "../../store";
import DonorsEmptyState from "./DonorsEmptyState.svelte"; import DonorsEmptyState from "./DonorsEmptyState.svelte";
@@ -77,11 +77,6 @@
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('total-donation-amount')} {$_('total-donation-amount')}
</th> </th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('total-paid-amount')}
</th>
<th scope="col" class="relative px-6 py-3"> <th scope="col" class="relative px-6 py-3">
<span class="sr-only">{$_('action')}</span> <span class="sr-only">{$_('action')}</span>
</th> </th>
@@ -132,7 +127,7 @@
<a <a
href="../donations/{d.id}" href="../donations/{d.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-600 text-white mr-1">{d.runner.firstname} class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-600 text-white mr-1">{d.runner.firstname}
{d.runner.middlename || ''} {d.runner.middlename}
{d.runner.lastname}</a> {d.runner.lastname}</a>
{:else} {:else}
<a <a
@@ -150,11 +145,6 @@
.toFixed(2) .toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })} .toLocaleString('de-DE', { valute: 'EUR' })}
</td> </td>
<td class="px-6 py-4 whitespace-nowrap">
{(donor.paidDonationAmount / 100)
.toFixed(2)
.toLocaleString('de-DE', { valute: 'EUR' })}
</td>
{#if active_deletes[donor.id] === true} {#if active_deletes[donor.id] === true}
<td <td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">

View File

@@ -32,12 +32,8 @@
target="_blank" target="_blank"
rel="noopener, noreferrer" rel="noopener, noreferrer"
href="https://git.odit.services/lfk/frontend/src/tag/{releaseinfo}">{releaseinfo}</a> href="https://git.odit.services/lfk/frontend/src/tag/{releaseinfo}">{releaseinfo}</a>
- -
<a <a class="underline" href="https://docs.lauf-fuer-kaya.de" target="_blank">{$_('documentation')}</a>
rel="noopener, noreferrer"
class="underline"
href="https://docs.lauf-fuer-kaya.de"
target="_blank">{$_('documentation')}</a>
- -
<a class="underline" href="/privacy">{$_('privacy')}</a> <a class="underline" href="/privacy">{$_('privacy')}</a>
- -

View File

@@ -19,7 +19,7 @@
) )
.then((resp) => { .then((resp) => {
Toastify({ Toastify({
text: $_('organization-deleted'), text: "Organization deleted",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();

View File

@@ -14,7 +14,7 @@
$: active_deletes = []; $: active_deletes = [];
$: sponsoring_contracts_show = current_organizations.some((r) => r.is_selected === true); $: sponsoring_contracts_show = current_organizations.some((r) => r.is_selected === true);
$: cards_show = current_organizations.some((r) => r.is_selected === true); $: cards_show = current_organizations.some((r) => r.is_selected === true);
$: generate_orgs = current_organizations.filter((r) => r.is_selected === true); $: generate_orgs = current_organizations.some((r) => r.is_selected === true);
$: certificates_show = current_organizations.some( $: certificates_show = current_organizations.some(
(r) => r.is_selected === true (r) => r.is_selected === true
); );

View File

@@ -1,418 +1,344 @@
<script> <script>
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import { import {
RunnerCardService, RunnerCardService,
RunnerOrganizationService, RunnerOrganizationService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js"; import Toastify from "toastify-js";
import { init } from "@paralleldrive/cuid2"; export let cards_show = false;
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); export let generate_cards = [];
export let generate_runners = [];
export let cards_show = false; export let generate_orgs = [];
export let generate_cards = []; export let generate_teams = [];
export let generate_runners = []; $: cards_dropdown_open = false;
export let generate_orgs = []; document.addEventListener("click", function (e) {
export let generate_teams = []; if (
$: cards_dropdown_open = false; e.target.parentNode?.parentNode?.id != "cards:dropdown" &&
document.addEventListener("click", function (e) { e.target.parentNode?.parentNode?.id != "cards:dropdown:menu"
if ( ) {
e.target.parentNode?.parentNode?.id != "cards:dropdown" && cards_dropdown_open = false;
e.target.parentNode?.parentNode?.id != "cards:dropdown:menu"
) {
cards_dropdown_open = false;
}
});
function generateRunnerCards(locale) {
cards_dropdown_open = false;
if (generate_orgs.length > 0) {
generateOrgCards(locale);
} else if (generate_teams.length > 0) {
generateTeamCards(locale);
} else if (generate_runners.length > 0) {
generateRunnersCards(locale);
} else {
generateCards(locale);
}
}
function generateCards(locale) {
const toast = Toastify({
text: $_("generating-pdf"),
duration: -1,
}).showToast();
fetch(
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(generate_cards),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
} }
}) });
.then((blob) => {
const url = window.URL.createObjectURL(blob); function generateRunnerCards(locale) {
let a = document.createElement("a"); cards_dropdown_open = false;
a.href = url;
a.download = `${$_("runnercards")}-${locale}-${createId()}.pdf`; if (generate_orgs.length > 0) {
document.body.appendChild(a); generateOrgCards(locale);
a.click(); } else if (generate_teams.length > 0) {
a.remove(); generateTeamCards(locale);
toast.hideToast(); } else if (generate_runners.length > 0) {
Toastify({ generateRunnersCards(locale);
text: $_("pdf-successfully-generated"), } else {
duration: 3500, generateCards(locale);
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", }
}
function generateCards(locale) {
const toast = Toastify({
text: $_("generating-pdf"),
duration: -1,
}).showToast(); }).showToast();
}) fetch(
.catch((err) => { `${config.baseurl}/documents/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
console.error(err); {
}); method: "POST",
} headers: {
"Content-Type": "application/json",
async function generateRunnersCards(locale) { },
const toast = Toastify({ body: JSON.stringify(generate_cards),
text: $_("generating-pdf"), }
duration: -1,
}).showToast();
const current_cards = await RunnerCardService.runnerCardControllerGetAll();
let cards = [];
for (let runner of generate_runners) {
let card = current_cards.find((c) => c.runner?.id == runner.id);
if (!card) {
card = await RunnerCardService.runnerCardControllerPost({
runner: runner.id,
});
}
cards.push(card);
}
fetch(
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cards),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
if (generate_runners.length == 1) {
a.download = `${$_("runnercards")}_${generate_runners[0].firstname}_${
generate_runners[0].lastname
}-${locale}-${createId()}.pdf`;
} else {
a.download = `${$_("runnercards")}-${locale}-${createId()}.pdf`;
}
document.body.appendChild(a);
a.click();
a.remove();
toast.hideToast();
Toastify({
text: $_("pdf-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
})
.catch((err) => {});
}
async function generateTeamCards(locale) {
const toast = Toastify({
text: $_("generating-pdfs"),
duration: -1,
}).showToast();
let count = 0;
const current_cards = await RunnerCardService.runnerCardControllerGetAll();
for (const t of generate_teams) {
const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
let cards = [];
for (let runner of runners) {
let card = current_cards.find((c) => c.runner?.id == runner.id);
if (!card) {
card = await RunnerCardService.runnerCardControllerPost({
runner: runner.id,
});
}
cards.push(card);
}
fetch(
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cards),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
count++;
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_("runnercards")}_${t.name}-${locale}-${createId()}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_teams.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
}
async function generateOrgCards(locale) {
const toast = Toastify({
text: $_("generating-pdfs"),
duration: -1,
}).showToast();
const current_cards = await RunnerCardService.runnerCardControllerGetAll();
let count = 0;
let count_orgs = 0;
for (const o of generate_orgs) {
count_orgs++;
let count = 0;
let runners =
await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id,
true
);
let cards = [];
for (let runner of runners) {
let card = current_cards.find((c) => c.runner?.id == runner.id);
if (!card) {
card = await RunnerCardService.runnerCardControllerPost({
runner: runner.id,
});
}
cards.push(card);
}
await fetch(
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cards),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_("runnercards")}_${o.name}_direct-${locale}-${createId()}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === o.teams.length && count_orgs === generate_orgs.length) {
toast.hideToast();
console.log("here");
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
for (const t of o.teams) {
count++;
let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
let cards = [];
for (let runner of runners) {
let card = current_cards.find((c) => c.runner?.id == runner.id);
if (!card) {
card = await RunnerCardService.runnerCardControllerPost({
runner: runner.id,
});
}
cards.push(card);
}
await fetch(
`${config.baseurl_documentserver}/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cards),
}
) )
.then((response) => { .then((response) => {
if (response.status != "200") { if (response.status != "200") {
toast.hideToast(); toast.hideToast();
Toastify({ Toastify({
text: $_("pdf-generation-failed"), text: $_("pdf-generation-failed"),
duration: 3500, duration: 3500,
backgroundColor: backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", "linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast(); }).showToast();
} else { } else {
return response.blob(); return response.blob();
} }
}) })
.then((blob) => { .then((blob) => {
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
let a = document.createElement("a"); let a = document.createElement("a");
a.href = url; a.href = url;
a.download = `${$_("runnercards")}_${o.name}_${ a.download = `${$_('runnercards')}-${locale}.pdf`;
t.name document.body.appendChild(a);
}-${locale}-${createId()}.pdf`; a.click();
document.body.appendChild(a); a.remove();
a.click(); toast.hideToast();
a.remove(); Toastify({
if ( text: $_("pdf-successfully-generated"),
count === o.teams.length && duration: 3500,
count_orgs === generate_orgs.length backgroundColor:
) { "linear-gradient(to right, #00b09b, #96c93d)",
toast.hideToast(); }).showToast();
Toastify({ })
text: $_("pdfs-successfully-generated"), .catch((err) => {
duration: 3500, console.error(err);
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", });
}).showToast(); }
}
}) async function generateRunnersCards(locale) {
.catch((err) => {}); const toast = Toastify({
} text: $_("generating-pdf"),
duration: -1,
}).showToast();
const current_cards = await RunnerCardService.runnerCardControllerGetAll();
let cards = [];
for (let runner of generate_runners) {
let card = current_cards.find((c) => c.runner?.id == runner.id);
if (!card) {
card = await RunnerCardService.runnerCardControllerPost({
runner: runner.id,
});
}
cards.push(card);
}
fetch(
`${config.baseurl}/documents/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cards),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
if(generate_runners.length == 1){
a.download = `${$_('runnercards')}_${generate_runners[0].firstname}_${generate_runners[0].lastname}-${locale}.pdf`;
}
else{
a.download = `Runnercards-${locale}.pdf`;
}
document.body.appendChild(a);
a.click();
a.remove();
toast.hideToast();
Toastify({
text: $_("pdf-successfully-generated"),
duration: 3500,
backgroundColor:
"linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
})
.catch((err) => {});
}
async function generateTeamCards(locale) {
const toast = Toastify({
text: $_("generating-pdfs"),
duration: -1,
}).showToast();
let count = 0;
const current_cards = await RunnerCardService.runnerCardControllerGetAll();
for (const t of generate_teams) {
const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
let cards = [];
for (let runner of runners) {
let card = current_cards.find((c) => c.runner?.id == runner.id);
if (!card) {
card = await RunnerCardService.runnerCardControllerPost({
runner: runner.id,
});
}
cards.push(card);
}
fetch(
`${config.baseurl}/documents/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cards),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
count++;
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_('runnercards')}_${t.name}-${locale}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_teams.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor:
"linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
}
async function generateOrgCards(locale) {
const toast = Toastify({
text: $_("generating-pdf"),
duration: -1,
}).showToast();
let count = 0;
const current_cards = await RunnerCardService.runnerCardControllerGetAll();
for (const o of generate_orgs) {
const runners = await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id
);
let cards = [];
for (let runner of runners) {
let card = current_cards.find((c) => c.runner?.id == runner.id);
if (!card) {
card = await RunnerCardService.runnerCardControllerPost({
runner: runner.id,
});
}
cards.push(card);
}
fetch(
`${config.baseurl}/documents/cards?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(cards),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
count++;
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_('runnercards')}_${o.name}-${locale}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_orgs.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor:
"linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
} }
}
</script> </script>
{#if cards_show} {#if cards_show}
<div id="cards:dropdown" class="relative inline-block"> <div id="cards:dropdown" class="relative inline-block">
<div> <div>
<button <button
on:click={() => { on:click={() => {
cards_dropdown_open = !cards_dropdown_open; cards_dropdown_open = !cards_dropdown_open;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex"
id="options-menu" id="options-menu"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="true" aria-expanded="true">
> {$_('generate-runnercards')}
{$_("generate-runnercards")} <svg
<svg xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" width="24"
width="24" height="24"
height="24" viewBox="0 0 24 24"
viewBox="0 0 24 24" class="-mr-1 ml-2 h-5 w-5"><path
class="-mr-1 ml-2 h-5 w-5" fill="none"
><path fill="none" d="M0 0h24v24H0z" /> d="M0 0h24v24H0z" />
<path <path
fill="currentColor" fill="currentColor"
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z" d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z" /></svg>
/></svg </button>
>
</button>
</div>
{#if cards_dropdown_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="cards:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateRunnerCards("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateRunnerCards("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div> </div>
</div> {#if cards_dropdown_open}
{/if} <div
</div> class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5"
id="cards:dropdown:menu">
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu">
<span
class="block w-full text-left px-4 py-2 text-sm text-gray-700">{$_('select-language')}</span>
<button
on:click={() => {
generateRunnerCards('de');
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem">
{$_('german')}
</button>
<button
on:click={() => {
generateRunnerCards('en');
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem">
{$_('english')}
</button>
</div>
</div>
{/if}
</div>
{/if} {/if}

View File

@@ -1,355 +1,277 @@
<script> <script>
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import { import {
DonationService, DonationService,
RunnerTeamService, RunnerTeamService,
RunnerOrganizationService, RunnerOrganizationService
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js"; import Toastify from "toastify-js";
import { init } from "@paralleldrive/cuid2"; export let certificates_show = false;
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); export let generate_runners = [];
export let generate_orgs = [];
export let certificates_show = false; export let generate_teams = [];
export let generate_runners = []; $: certificates_dropdown_open = false;
export let generate_orgs = []; document.addEventListener("click", function (e) {
export let generate_teams = []; if (
$: certificates_dropdown_open = false; e.target.parentNode?.parentNode?.id != "certificates:dropdown" &&
document.addEventListener("click", function (e) { e.target.parentNode?.parentNode?.id != "certificates:dropdown:menu"
if ( ) {
e.target.parentNode?.parentNode?.id != "certificates:dropdown" && certificates_dropdown_open = false;
e.target.parentNode?.parentNode?.id != "certificates:dropdown:menu"
) {
certificates_dropdown_open = false;
}
});
function generateCertificates(locale) {
certificates_dropdown_open = false;
if (generate_orgs.length > 0) {
generateOrgCertificates(locale);
} else if (generate_teams.length > 0) {
generateTeamCertificates(locale);
} else {
generateRunnerCertificates(locale);
}
}
async function generateRunnerCertificates(locale) {
const toast = Toastify({
text: $_("generating-pdf"),
duration: -1,
}).showToast();
const current_donations =
(await DonationService.donationControllerGetAll()) || [];
let certificateRunners = [];
for (let runner of generate_runners) {
runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || [];
console.log(runner.distanceDonations);
certificateRunners.push(runner);
}
fetch(
`${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(certificateRunners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
} }
}) });
.then((blob) => {
const url = window.URL.createObjectURL(blob); function generateCertificates(locale) {
let a = document.createElement("a"); certificates_dropdown_open = false;
a.href = url;
if (generate_runners.length == 1) { if (generate_orgs.length > 0) {
a.download = `${$_("certificates")}_${ generateOrgCertificates(locale);
generate_runners[0].firstname } else if (generate_teams.length > 0) {
}_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`; generateTeamCertificates(locale);
} else { } else {
a.download = `${$_("certificates")}-${locale}.pdf`; generateRunnerCertificates(locale);
} }
document.body.appendChild(a); }
a.click();
a.remove(); async function generateRunnerCertificates(locale) {
toast.hideToast(); const toast = Toastify({
Toastify({ text: $_("generating-pdf"),
text: $_("pdf-successfully-generated"), duration: -1,
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
}) const current_donations = await DonationService.donationControllerGetAll();
.catch((err) => {});
}
async function generateTeamCertificates(locale) {
const toast = Toastify({
text: $_("generating-pdfs"),
duration: -1,
}).showToast();
let count = 0;
const current_donations =
(await DonationService.donationControllerGetAll()) || [];
for (const t of generate_teams) {
const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
let certificateRunners = [];
for (let runner of runners) {
runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner);
}
fetch(
`${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(certificateRunners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
count++;
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_("certificates")}_${t.name}-${locale}-${createId()}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_teams.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
}
async function generateOrgCertificates(locale) {
const toast = Toastify({
text: $_("generating-pdfs"),
duration: -1,
}).showToast();
const current_donations =
(await DonationService.donationControllerGetAll()) || [];
let count = 0;
let count_orgs = 0;
for (const o of generate_orgs) {
count_orgs++;
let count = 0;
let runners =
await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id,
true
);
let certificateRunners = [];
for (let runner of runners) {
runner.distanceDonations =
current_donations.filter((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner);
}
await fetch(
`${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(certificateRunners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_("certificates")}_${o.name}-${locale}-${createId()}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === o.teams.length && count_orgs === generate_orgs.length) {
toast.hideToast();
console.log("here");
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
for (const t of o.teams) {
count++;
let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
let certificateRunners = []; let certificateRunners = [];
for (let runner of runners) { for (let runner of generate_runners) {
runner.distanceDonations = runner.distanceDonations = current_donations.find((d) => d.runner?.id == runner.id) || [];
current_donations.filter((d) => d.runner?.id == runner.id) || []; certificateRunners.push(runner);
certificateRunners.push(runner);
} }
await fetch( fetch(
`${config.baseurl_documentserver}/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`, `${config.baseurl}/documents/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
{ {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(certificateRunners), body: JSON.stringify(certificateRunners),
} }
) )
.then((response) => { .then((response) => {
if (response.status != "200") { if (response.status != "200") {
toast.hideToast(); toast.hideToast();
Toastify({ Toastify({
text: $_("pdf-generation-failed"), text: $_("pdf-generation-failed"),
duration: 3500, duration: 3500,
backgroundColor: backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)", "linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast(); }).showToast();
} else { } else {
return response.blob(); return response.blob();
} }
}) })
.then((blob) => { .then((blob) => {
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
let a = document.createElement("a"); let a = document.createElement("a");
a.href = url; a.href = url;
a.download = `${$_("certificates")}_${o.name}_${ if(generate_runners.length == 1){
t.name a.download = `${$_('certificates')}_${generate_runners[0].firstname}_${generate_runners[0].lastname}-${locale}.pdf`;
}-${locale}-${createId()}.pdf`; }
document.body.appendChild(a); else{
a.click(); a.download = `${$_('certificates')}-${locale}.pdf`;
a.remove(); }
if ( document.body.appendChild(a);
count === o.teams.length && a.click();
count_orgs === generate_orgs.length a.remove();
) { toast.hideToast();
toast.hideToast(); Toastify({
Toastify({ text: $_("pdf-successfully-generated"),
text: $_("pdfs-successfully-generated"), duration: 3500,
duration: 3500, backgroundColor:
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
} })
}) .catch((err) => {});
.catch((err) => {}); }
}
async function generateTeamCertificates(locale) {
const toast = Toastify({
text: $_("generating-pdfs"),
duration: -1,
}).showToast();
let count = 0;
const current_donations = await DonationService.donationControllerGetAll();
for (const t of generate_teams) {
const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
let certificateRunners = [];
for (let runner of runners) {
runner.distanceDonations = current_donations.find((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner);
}
fetch(
`${config.baseurl}/documents/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(certificateRunners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
count++;
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_('certificates')}_${t.name}-${locale}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_teams.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor:
"linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
}
async function generateOrgCertificates(locale) {
const toast = Toastify({
text: $_("generating-pdf"),
duration: -1,
}).showToast();
let count = 0;
const current_donations = await DonationService.donationControllerGetAll();
for (const o of generate_orgs) {
const runners = await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id
);
let certificateRunners = [];
for (let runner of runners) {
runner.distanceDonations = current_donations.find((d) => d.runner?.id == runner.id) || [];
certificateRunners.push(runner);
}
fetch(
`${config.baseurl}/documents/certificates?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(certificateRunners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
count++;
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_('certificates')}_${o.name}-${locale}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_orgs.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor:
"linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
} }
}
</script> </script>
{#if certificates_show} {#if certificates_show}
<div id="certificates:dropdown" class="relative inline-block"> <div id="certificates:dropdown" class="relative inline-block">
<div> <div>
<button <button
on:click={() => { on:click={() => {
certificates_dropdown_open = !certificates_dropdown_open; certificates_dropdown_open = !certificates_dropdown_open;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex"
id="options-menu" id="options-menu"
aria-haspopup="true" aria-haspopup="true"
aria-expanded="true" aria-expanded="true">
> {$_('generate-runner-certificates')}
{$_("generate-runner-certificates")} <svg
<svg xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg" width="24"
width="24" height="24"
height="24" viewBox="0 0 24 24"
viewBox="0 0 24 24" class="-mr-1 ml-2 h-5 w-5"><path
class="-mr-1 ml-2 h-5 w-5" fill="none"
><path fill="none" d="M0 0h24v24H0z" /> d="M0 0h24v24H0z" />
<path <path
fill="currentColor" fill="currentColor"
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z" d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z" /></svg>
/></svg </button>
>
</button>
</div>
{#if certificates_dropdown_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="certificates:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateCertificates("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateCertificates("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div> </div>
</div> {#if certificates_dropdown_open}
{/if} <div
</div> class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5"
id="certificates:dropdown:menu">
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu">
<span
class="block w-full text-left px-4 py-2 text-sm text-gray-700">{$_('select-language')}</span>
<button
on:click={() => {
generateCertificates('de');
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem">
{$_('german')}
</button>
<button
on:click={() => {
generateCertificates('en');
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem">
{$_('english')}
</button>
</div>
</div>
{/if}
</div>
{/if} {/if}

View File

@@ -1,324 +1,257 @@
<script> <script>
import { getLocaleFromNavigator, _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import { import {
RunnerOrganizationService, RunnerOrganizationService,
RunnerTeamService, RunnerTeamService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import Toastify from "toastify-js"; import Toastify from "toastify-js";
import { init } from "@paralleldrive/cuid2"; export let sponsoring_contracts_show = false;
const createId = init({ length: 10, fingerprint: "lfk-frontend" }); export let generate_runners = [];
export let generate_orgs = [];
export let sponsoring_contracts_show = false; export let generate_teams = [];
export let generate_runners = []; $: sponsoring_contracts_download_open = false;
export let generate_orgs = []; document.addEventListener("click", function (e) {
export let generate_teams = []; if (
$: sponsoring_contracts_download_open = false; e.target.parentNode?.parentNode?.id != "sponsoring:dropdown" &&
document.addEventListener("click", function (e) { e.target.parentNode?.parentNode?.id != "sponsoring:dropdown:menu"
if ( ) {
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown" && sponsoring_contracts_download_open = false;
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown:menu"
) {
sponsoring_contracts_download_open = false;
}
});
function generateSponsoringContract(locale) {
sponsoring_contracts_download_open = false;
if (generate_orgs.length > 0) {
generateOrgContracts(locale);
} else if (generate_teams.length > 0) {
generateTeamContracts(locale);
} else {
generateRunnerContracts(locale);
}
}
async function generateTeamContracts(locale) {
const toast = Toastify({
text: $_("generating-pdfs"),
duration: -1,
}).showToast();
let count = 0;
for (const t of generate_teams) {
count++;
const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
fetch(
`${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(runners),
} }
) });
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_("sponsorings")}_${t.name}-${locale}-${createId()}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_teams.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
}
async function generateOrgContracts(locale) { function generateSponsoringContract(locale) {
const toast = Toastify({ sponsoring_contracts_download_open = false;
text: $_("generating-pdf"),
duration: -1,
}).showToast();
let count_orgs = 0;
for (const o of generate_orgs) {
count_orgs++;
let count = 0;
let runners =
await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id,
true
);
await fetch(
`${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(runners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_("sponsorings")}_${o.name}_direct-${locale}-${createId()}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === o.teams.length && count_orgs === generate_orgs.length) {
toast.hideToast();
console.log("here");
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
for (const t of o.teams) {
count++;
let runners = await RunnerTeamService.runnerTeamControllerGetRunners(
t.id
);
await fetch(
`${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(runners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_("sponsorings")}_${o.name}_${
t.name
}-${locale}-${createId()}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (
count === o.teams.length &&
count_orgs === generate_orgs.length
) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
}
}
function generateRunnerContracts(locale) { if (generate_orgs.length > 0) {
const toast = Toastify({ generateOrgContracts(locale);
text: $_("generating-pdf"), } else if (generate_teams.length > 0) {
duration: -1, generateTeamContracts(locale);
}).showToast();
fetch(
`${config.baseurl_documentserver}/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(generate_runners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else { } else {
return response.blob(); generateRunnerContracts(locale);
} }
}) }
.then((blob) => {
const url = window.URL.createObjectURL(blob); async function generateTeamContracts(locale) {
let a = document.createElement("a"); const toast = Toastify({
a.href = url; text: $_("generating-pdfs"),
if (generate_runners.length == 1) { duration: -1,
a.download = `${$_("sponsorings")}_${generate_runners[0].firstname}_${
generate_runners[0].lastname
}-${locale}-${createId()}.pdf`;
}
a.download = `${$_("sponsorings")}-${locale}-${createId()}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
toast.hideToast();
Toastify({
text: $_("pdf-successfully-generated"),
duration: 3500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
}) let count = 0;
.catch((err) => { for (const t of generate_teams) {
console.error(err); count++;
}); const runners = await RunnerTeamService.runnerTeamControllerGetRunners(
} t.id
);
fetch(
`${config.baseurl}/documents/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(runners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_('sponsorings')}_${t.name}-${locale}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_teams.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor:
"linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
}
async function generateOrgContracts(locale) {
const toast = Toastify({
text: $_("generating-pdf"),
duration: -1,
}).showToast();
for (const o of generate_orgs) {
const runners = await RunnerOrganizationService.runnerOrganizationControllerGetRunners(
o.id
);
fetch(
`${config.baseurl}/documents/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(runners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
count++;
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
a.download = `${$_('sponsorings')}_${o.name}-${locale}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
if (count === generate_orgs.length) {
toast.hideToast();
Toastify({
text: $_("pdfs-successfully-generated"),
duration: 3500,
backgroundColor:
"linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
}
})
.catch((err) => {});
}
}
function generateRunnerContracts(locale) {
const toast = Toastify({
text: $_("generating-pdf"),
duration: -1,
}).showToast();
fetch(
`${config.baseurl}/documents/contracts?locale=${locale}&download=true&key=${config.documentserver_key}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(generate_runners),
}
)
.then((response) => {
if (response.status != "200") {
toast.hideToast();
Toastify({
text: $_("pdf-generation-failed"),
duration: 3500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
} else {
return response.blob();
}
})
.then((blob) => {
const url = window.URL.createObjectURL(blob);
let a = document.createElement("a");
a.href = url;
if(generate_runners.length == 1){
a.download = `${$_('sponsorings')}_${generate_runners[0].firstname}_${generate_runners[0].lastname}-${locale}.pdf`;
}
a.download = `${$_('sponsorings')}-${locale}.pdf`;
document.body.appendChild(a);
a.click();
a.remove();
toast.hideToast();
Toastify({
text: $_("pdf-successfully-generated"),
duration: 3500,
backgroundColor:
"linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
})
.catch((err) => {
console.error(err);
});
}
</script> </script>
{#if sponsoring_contracts_show} {#if sponsoring_contracts_show}
<div id="sponsoring:dropdown" class="relative inline-block"> <div id="sponsoring:dropdown" class="relative inline-block">
<div> <div>
<button <button
on:click={() => { on:click={() => {
sponsoring_contracts_download_open = sponsoring_contracts_download_open = !sponsoring_contracts_download_open;
!sponsoring_contracts_download_open; }}
}} type="button"
type="button" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm inline-flex" id="options-menu"
id="options-menu" aria-haspopup="true"
aria-haspopup="true" aria-expanded="true">
aria-expanded="true" {$_('generate-sponsoring-contracts')}
> <svg
{$_("generate-sponsoring-contracts")} xmlns="http://www.w3.org/2000/svg"
<svg width="24"
xmlns="http://www.w3.org/2000/svg" height="24"
width="24" viewBox="0 0 24 24"
height="24" class="-mr-1 ml-2 h-5 w-5"><path
viewBox="0 0 24 24" fill="none"
class="-mr-1 ml-2 h-5 w-5" d="M0 0h24v24H0z" />
><path fill="none" d="M0 0h24v24H0z" /> <path
<path fill="currentColor"
fill="currentColor" d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z" /></svg>
d="M3 19h18v2H3v-2zm10-5.83l6.07-6.07 1.42 1.41L12 17 3.52 8.52l1.4-1.42L11 13.17V2h2v11.17z" </button>
/></svg
>
</button>
</div>
{#if sponsoring_contracts_download_open}
<div
class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-10"
id="sponsoring:dropdown:menu"
>
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu"
>
<span class="block w-full text-left px-4 py-2 text-sm text-gray-700"
>{$_("select-language")}</span
>
<button
on:click={() => {
generateSponsoringContract("de");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("german")}
</button>
<button
on:click={() => {
generateSponsoringContract("en");
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem"
>
{$_("english")}
</button>
</div> </div>
</div> {#if sponsoring_contracts_download_open}
{/if} <div
</div> class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5"
id="sponsoring:dropdown:menu">
<div
class="py-1"
role="menu"
aria-orientation="vertical"
aria-labelledby="options-menu">
<span
class="block w-full text-left px-4 py-2 text-sm text-gray-700">{$_('select-language')}</span>
<button
on:click={() => {
generateSponsoringContract('de');
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem">
{$_('german')}
</button>
<button
on:click={() => {
generateSponsoringContract('en');
}}
type="submit"
class="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900"
role="menuitem">
{$_('english')}
</button>
</div>
</div>
{/if}
</div>
{/if} {/if}

View File

@@ -202,7 +202,7 @@
toast.hideToast(); toast.hideToast();
recent_processed = true; recent_processed = true;
Toastify({ Toastify({
text: $_('import-finished'), text: "Import finished",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();

View File

@@ -71,9 +71,6 @@
}).showToast(); }).showToast();
let postdata = {}; let postdata = {};
postdata = Object.assign(postdata, editable); postdata = Object.assign(postdata, editable);
if (postdata.phone === "") {
postdata.phone = null;
}
RunnerService.runnerControllerPut(original_data.id, postdata) RunnerService.runnerControllerPut(original_data.id, postdata)
.then((resp) => { .then((resp) => {
Object.assign(original_data, editable); Object.assign(original_data, editable);
@@ -98,7 +95,7 @@
</script> </script>
{#await runner_promise} {#await runner_promise}
{$_("loading-runners")} {$_('loading-runners')}
{:then} {:then}
<section class="container p-5 select-none"> <section class="container p-5 select-none">
<div class="flex flex-row mb-4"> <div class="flex flex-row mb-4">
@@ -112,15 +109,12 @@
class="flex-shrink-0 w-5 h-5 mr-2" class="flex-shrink-0 w-5 h-5 mr-2"
fill="currentColor" fill="currentColor"
width="24" width="24"
height="24" height="24"><path fill="none" d="M0 0h24v24H0z" />
><path fill="none" d="M0 0h24v24H0z" />
<path <path
d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" d="M9.83 8.79L8 9.456V13H6V8.05h.015l5.268-1.918c.244-.093.51-.14.782-.131a2.616 2.616 0 0 1 2.427 1.82c.186.583.356.977.51 1.182A4.992 4.992 0 0 0 19 11v2a6.986 6.986 0 0 1-5.402-2.547l-.581 3.297L15 15.67V23h-2v-5.986l-2.05-1.987-.947 4.298-6.894-1.215.348-1.97 4.924.868L9.83 8.79zM13.5 5.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" /></svg>
/></svg
>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<a class="mr-2" href="./">{$_("runners")}</a><svg <a class="mr-2" href="./">{$_('runners')}</a><svg
stroke="currentColor" stroke="currentColor"
fill="none" fill="none"
stroke-width="2" stroke-width="2"
@@ -130,17 +124,17 @@
class="h-3 w-3 mr-2 stroke-current" class="h-3 w-3 mr-2 stroke-current"
height="1em" height="1em"
width="1em" width="1em"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"><line
><line x1="5" y1="12" x2="19" y2="12" /> x1="5"
<polyline points="12 5 19 12 12 19" /></svg y1="12"
> x2="19"
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li> </li>
<li class="flex items-center"> <li class="flex items-center">
<span class="mr-2" <span class="mr-2">{original_data.firstname}
>{original_data.firstname} {original_data.middlename || ''}
{original_data.middlename || ""} {original_data.lastname}</span>
{original_data.lastname}</span
>
</li> </li>
</ol> </ol>
</nav> </nav>
@@ -148,42 +142,36 @@
</div> </div>
<div class="mb-8 text-3xl font-extrabold leading-tight"> <div class="mb-8 text-3xl font-extrabold leading-tight">
{original_data.firstname} {original_data.firstname}
{original_data.middlename || ""} {original_data.middlename || ''}
{original_data.lastname} {original_data.lastname}
<span data-id="runner_actions_${editable.id}"> <span data-id="runner_actions_${editable.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:DELETE")} {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:DELETE')}
{#if delete_triggered} {#if delete_triggered}
<button <button
on:click={deleteRunner} on:click={deleteRunner}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 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 justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-deletion')}</button>
>{$_("confirm-deletion")}</button
>
<button <button
on:click={() => { on:click={() => {
delete_triggered = !delete_triggered; delete_triggered = !delete_triggered;
}} }}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm" class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button>
>{$_("cancel")}</button
>
{/if} {/if}
<GenerateSponsoringContracts <GenerateSponsoringContracts
bind:sponsoring_contracts_show bind:sponsoring_contracts_show
bind:generate_runners bind:generate_runners />
/> <GenerateRunnerCards
<GenerateRunnerCards bind:cards_show bind:generate_runners /> bind:cards_show
bind:generate_runners />
<GenerateRunnerCertificates <GenerateRunnerCertificates
bind:certificates_show bind:certificates_show
bind:generate_runners bind:generate_runners />
/>
{#if !delete_triggered} {#if !delete_triggered}
<button <button
on:click={() => { on:click={() => {
delete_triggered = true; delete_triggered = true;
}} }}
type="button" type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 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 justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-runner')}</button>
>{$_("delete-runner")}</button
>
{/if} {/if}
{/if} {/if}
{#if !delete_triggered} {#if !delete_triggered}
@@ -192,128 +180,121 @@
class:opacity-50={!save_enabled} class:opacity-50={!save_enabled}
type="button" type="button"
on:click={submit} on:click={submit}
class="w-full 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 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">{$_('save-changes')}</button>
>{$_("save-changes")}</button
>
{/if} {/if}
</span> </span>
</div> </div>
<!-- --> <!-- -->
<div class="text-sm w-full"> <div class="text-sm w-full">
<label for="firstname" class="font-medium text-gray-700" <label
>{$_("first-name")}</label for="firstname"
> class="font-medium text-gray-700">{$_('first-name')}</label>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_("first-name")} placeholder={$_('first-name')}
type="text" type="text"
class:border-red-500={!isFirstnameValid} class:border-red-500={!isFirstnameValid}
class:focus:border-red-500={!isFirstnameValid} class:focus:border-red-500={!isFirstnameValid}
class:focus:ring-red-500={!isFirstnameValid} class:focus:ring-red-500={!isFirstnameValid}
bind:value={editable.firstname} bind:value={editable.firstname}
name="firstname" name="firstname"
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 rounded-md 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 rounded-md p-2" />
/>
{#if !isFirstnameValid} {#if !isFirstnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
> {$_('first-name-is-required')}
{$_("first-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label for="middlename" class="font-medium text-gray-700" <label
>{$_("middle-name")}</label for="middlename"
> class="font-medium text-gray-700">{$_('middle-name')}</label>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_("middle-name")} placeholder={$_('middle-name')}
type="text" type="text"
bind:value={editable.middlename} bind:value={editable.middlename}
name="middlename" name="middlename"
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 rounded-md 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 rounded-md p-2" />
/>
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label for="lastname" class="font-medium text-gray-700" <label
>{$_("last-name")}</label for="lastname"
> class="font-medium text-gray-700">{$_('last-name')}</label>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_("last-name")} placeholder={$_('last-name')}
type="text" type="text"
bind:value={editable.lastname} bind:value={editable.lastname}
class:border-red-500={!isLastnameValid} class:border-red-500={!isLastnameValid}
class:focus:border-red-500={!isLastnameValid} class:focus:border-red-500={!isLastnameValid}
class:focus:ring-red-500={!isLastnameValid} class:focus:ring-red-500={!isLastnameValid}
name="lastname" name="lastname"
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 rounded-md 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 rounded-md p-2" />
/>
{#if !isLastnameValid} {#if !isLastnameValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
> {$_('last-name-is-required')}
{$_("last-name-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label for="email" class="font-medium text-gray-700" <label
>{$_("e-mail-adress")}</label for="email"
> class="font-medium text-gray-700">{$_('e-mail-adress')}</label>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_("e-mail-adress")} placeholder={$_('e-mail-adress')}
type="email" type="email"
bind:value={editable.email} bind:value={editable.email}
class:border-red-500={!isEmailValid} class:border-red-500={!isEmailValid}
class:focus:border-red-500={!isEmailValid} class:focus:border-red-500={!isEmailValid}
class:focus:ring-red-500={!isEmailValid} class:focus:ring-red-500={!isEmailValid}
name="email" name="email"
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 rounded-md 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 rounded-md p-2" />
/>
{#if !isEmailValid} {#if !isEmailValid}
<span <span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1" class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
> {$_('valid-email-is-required')}
{$_("valid-email-is-required")}
</span> </span>
{/if} {/if}
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<label for="phone" class="font-medium text-gray-700">{$_("phone")}</label> <label for="phone" class="font-medium text-gray-700">{$_('phone')}</label>
<input <input
autocomplete="off" autocomplete="off"
placeholder={$_("phone")} placeholder={$_('phone')}
type="tel" type="tel"
bind:value={editable.phone} bind:value={editable.phone}
name="phone" name="phone"
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 rounded-md 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 rounded-md p-2" />
/>
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<span class="font-medium text-gray-700">{$_("group")}</span> <span class="font-medium text-gray-700">{$_('group')}</span>
<Select <Select
containerClasses="rounded-l-md 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 rounded-md p-2" containerClasses="rounded-l-md 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 rounded-md p-2"
itemFilter={(label, filterText, option) => itemFilter={(label, filterText, option) => label
label.toLowerCase().includes(filterText.toLowerCase()) || .toLowerCase()
option.id.value.toString().startsWith(filterText.toLowerCase())} .includes(
filterText.toLowerCase()
) || option.id.value
.toString()
.startsWith(filterText.toLowerCase())}
items={groups} items={groups}
showChevron={true} showChevron={true}
placeholder={$_("search-for-an-organization-or-team-by-name-or-id")} placeholder={$_('search-for-an-organization-or-team-by-name-or-id')}
noOptionsMessage={$_("no-organization-or-team-found")} noOptionsMessage={$_('no-organization-or-team-found')}
bind:selectedValue={group} bind:selectedValue={group}
on:select={(selectedValue) => { on:select={(selectedValue) => {
editable.group = selectedValue.detail.value.id; editable.group = selectedValue.detail.value.id;
}} }}
on:clear={() => (editable.group = null)} on:clear={() => (editable.group = null)} />
/>
</div> </div>
<div class="text-sm w-full"> <div class="text-sm w-full">
<span class="font-medium text-gray-700">{$_("distance")}</span> <span class="font-medium text-gray-700">{$_('distance')}</span>
<br /> <br />
<span class="text-gray-700">{original_data.distance / 1000} km</span> <span class="text-gray-700">{original_data.distance} km</span>
</div> </div>
</section> </section>
{:catch error} {:catch error}

View File

@@ -1,26 +1,37 @@
<script> <script>
import { _ } from "svelte-i18n"; import { getLocaleFromNavigator, _ } from "svelte-i18n";
import { import {
RunnerService, RunnerService,
RunnerTeamService, RunnerTeamService,
RunnerOrganizationService, RunnerOrganizationService,
} from "@odit/lfk-client-js"; } from "@odit/lfk-client-js";
import ThFilterGroup from "./ThFilterGroup.svelte";
import { DataHandler, Datatable, Th, ThFilter } from "@vincjo/datatables";
import store from "../../store"; import store from "../../store";
import RunnersEmptyState from "./RunnersEmptyState.svelte";
import Select from "svelte-select";
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte"; import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte"; import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte"; import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
import { onMount } from "svelte"; $: searchvalue = "";
$: active_deletes = []; $: active_deletes = [];
let dataLoaded = false; export let current_runners = [];
let current_runners = []; const runners_promise = RunnerService.runnerControllerGetAll().then((val) => {
const handler = new DataHandler(current_runners, { rowsPerPage: 50 }); current_runners = val;
const rows = handler.getRows(); });
$: sponsoring_contracts_show = generate_runners.length > 0; $: selectedFilter_teams = null;
$: cards_show = generate_runners.length > 0; $: selectedFilter = null;
$: certificates_show = generate_runners.length > 0; $: filter__teams = selectedFilter_teams || [];
$: generate_runners = []; //current_runners.filter((r) => r.selected === true); $: filter__orgs = selectedFilter || [];
$: filterGroupIDs = filter__teams.concat(filter__orgs).map((i) => i.value);
$: sponsoring_contracts_show = current_runners.some(
(r) => r.is_selected === true
);
$: cards_show = current_runners.some(
(r) => r.is_selected === true
);
$: certificates_show = current_runners.some(
(r) => r.is_selected === true
);
$: generate_runners = current_runners.filter((r) => r.is_selected === true);
$: teams = []; $: teams = [];
$: orgs = []; $: orgs = [];
$: mappedteams = teams.map(function (g) { $: mappedteams = teams.map(function (g) {
@@ -31,193 +42,222 @@
return { value: g.id, label: g.name }; return { value: g.id, label: g.name };
}) })
.concat(mappedteams); .concat(mappedteams);
onMount(() => {
RunnerService.runnerControllerGetAll().then((val) => { RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
current_runners = val; teams = val;
dataLoaded = true;
handler.setRows(val);
});
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
teams = val;
});
RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
(val) => {
orgs = val;
}
);
}); });
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
orgs = val;
});
function should_display_based_on_id(id) {
if (searchvalue.toString().slice(-1) === "*") {
return id.toString().startsWith(searchvalue.replace("*", ""));
}
return id.toString() === searchvalue;
}
</script> </script>
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")} {#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')}
{#if !dataLoaded} {#await runners_promise}
<div <div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2" class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
role="alert" role="alert">
> <p class="font-bold">{$_('runners-are-being-loaded')}</p>
<p class="font-bold">{$_("runners-are-being-loaded")}</p> <p class="text-sm">{$_('this-might-take-a-moment')}</p>
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
</div> </div>
{:else} {:then}
<div class="h-12"> {#if current_runners.length === 0}
<GenerateSponsoringContracts <RunnersEmptyState />
bind:sponsoring_contracts_show {:else}
bind:generate_runners <input
/> type="search"
<GenerateRunnerCards bind:cards_show bind:generate_runners /> bind:value={searchvalue}
<GenerateRunnerCertificates placeholder={$_('datatable.search')}
bind:certificates_show aria-label={$_('datatable.search')}
bind:generate_runners class="gridjs-input gridjs-search-input mb-4" />
/> <div class="block mb-6">
</div> <label
<Datatable {handler}> for="country"
<table> class="text-sm font-medium text-gray-700">{$_('filter-by-organization-team')}</label>
<thead> <Select
<tr> on:select={(event) => {
<th> selectedFilter = event.detail;
<input }}
type="checkbox" selectedValue={selectedFilter}
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" placeholder={$_('filter-by-organization-team')}
checked={generate_runners.length == current_runners.length} containerClasses="mt-1 py-2 px-3 border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm"
on:click={() => { items={selectgroups}
if (generate_runners.length != current_runners.length) { isMulti={true} />
generate_runners = current_runners; </div>
} else { <div class="h-12">
generate_runners = []; <GenerateSponsoringContracts
} bind:sponsoring_contracts_show
}} bind:generate_runners />
/> <GenerateRunnerCards
</th> bind:cards_show
<Th {handler} orderBy="id">ID</Th> bind:generate_runners />
<Th {handler} orderBy="firstname">First Name</Th> <GenerateRunnerCertificates
<Th {handler} orderBy="middlename">Middle Name</Th> bind:certificates_show
<Th {handler} orderBy="lastname">Last Name</Th> bind:generate_runners />
<th>Gruppe</th> </div>
<Th {handler} orderBy="distance">Distanz</Th> <div
<th>{$_("action")}</th> class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
</tr> <table class="divide-y divide-gray-200 w-full">
<tr> <thead class="bg-gray-50">
<th />
<ThFilter {handler} filterBy="id" />
<ThFilter {handler} filterBy="firstname" />
<ThFilter {handler} filterBy="middlename" />
<ThFilter {handler} filterBy="lastname" />
<ThFilterGroup groups={selectgroups} {handler} />
<th />
<th />
</tr>
</thead>
<tbody>
{#each $rows as row}
<tr> <tr>
<td> <th
<input scope="col"
type="checkbox" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" <span
checked={generate_runners.filter((i)=>i.id == row.id).length > 0}
on:click={() => { on:click={() => {
if ( const newstate = !current_runners.some((r) => r.is_selected === true);
generate_runners.findIndex((i) => i.id == row.id) == -1 current_runners = current_runners.map((r) => {
) { r.is_selected = newstate;
generate_runners.push(row); return r;
generate_runners = generate_runners; });
} else {
generate_runners = generate_runners.filter(
(r) => r.id != row.id
);
}
console.log(generate_runners)
}} }}
/> class="underline cursor-pointer select-none">{#if current_runners.some((r) => r.is_selected === true)}
</td> {$_('deselect-all')}
<td>{row.id}</td> {:else}{$_('select-all')}{/if}
<td>{row.firstname}</td> </span>
<td>{row.middlename || ""}</td> </th>
<td>{row.lastname}</td> <th
<td scope="col"
>{#if row.group.responseType === "RUNNERTEAM"} class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<a {$_('name')}
href="../teams/{row.group.id}" </th>
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800" <th
>{row.group.parentGroup.name} &gt; {row.group.name}</a scope="col"
> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{/if} {$_('contact-information')}
{#if row.group.responseType === "RUNNERORGANIZATION"} </th>
<a <th
href="../orgs/{row.group.id}" scope="col"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
>{row.group.name}</a {$_('group')}
> </th>
{/if}</td <th
> scope="col"
<td>{row.distance / 1000} km</td> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
<td> {$_('distance-in-km')}
{#if active_deletes[row.id] === true} </th>
<button <th scope="col" class="relative px-6 py-3">
on:click={() => { <span class="sr-only">{$_('action')}</span>
active_deletes[row.id] = false; </th>
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer"
>{$_("cancel-delete")}</button
>
<button
on:click={() => {
RunnerService.runnerControllerRemove(row.id, true)
.then((resp) => {
current_runners = current_runners.filter(
(obj) => obj.id !== row.id
);
})
.catch((err) => {});
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("confirm-delete")}</button
>
{:else}
<a
href="./{row.id}"
class="text-indigo-600 hover:text-indigo-900"
>{$_("details")}</a
>
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:DELETE")}
<button
on:click={() => {
active_deletes[row.id] = true;
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
>{$_("delete")}</button
>
{/if}
{/if}
</td>
</tr> </tr>
{/each} </thead>
</tbody> <tbody class="divide-y divide-gray-200">
</table> {#each current_runners as runner}
</Datatable> {#if runner.firstname
{/if} .toLowerCase()
.includes(
searchvalue.toLowerCase()
) || runner.lastname
.toLowerCase()
.includes(
searchvalue.toLowerCase()
) || should_display_based_on_id(runner.id)}
{#if filterGroupIDs.includes(runner.group.id) || filterGroupIDs.includes(runner.group.parentGroup?.id) || filterGroupIDs.length === 0}
<tr
data-rowid="user_{runner.id}"
data-groupid={runner.group.id}>
<td class="px-6 py-4 whitespace-nowrap">
<input
bind:checked={runner.is_selected}
type="checkbox"
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
</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">
{runner.firstname}
{runner.middlename || ''}
{runner.lastname}
</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
{#if runner.email}
<div class="text-sm text-gray-500">{runner.email}</div>
{/if}
{#if runner.phone}
<div class="text-sm text-gray-500">{runner.phone}</div>
{/if}
{#if runner.address.address1 !== null}
{runner.address.address1}<br />
{runner.address.address2 || ''}<br />
{runner.address.postalcode}
{runner.address.city}
{runner.address.country}
{/if}
</td>
<td class="px-6 py-4 whitespace-nowrap">
{#if runner.group.responseType === 'RUNNERTEAM'}
<a
href="../teams/{runner.group.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{runner.group.name}</a>
{/if}
{#if runner.group.responseType === 'RUNNERORGANIZATION'}
<a
href="../orgs/{runner.group.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{runner.group.name}</a>
{/if}
</td>
<td class="px-6 py-4 whitespace-nowrap">
{runner.distance}
</td>
{#if active_deletes[runner.id] === true}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
on:click={() => {
active_deletes[runner.id] = false;
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
<button
on:click={() => {
RunnerService.runnerControllerRemove(runner.id, true)
.then((resp) => {
current_runners = current_runners.filter((obj) => obj.id !== runner.id);
})
.catch((err) => {});
}}
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="./{runner.id}"
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a>
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:DELETE')}
<button
on:click={() => {
active_deletes[runner.id] = true;
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button>
{/if}
</td>
{/if}
</tr>
{/if}
{/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} {/if}
<style>
thead {
background: #fff;
}
thead {
position: sticky;
inset-block-start: 0;
}
tbody td {
padding: 4px;
}
tbody tr:nth-child(even) {
background: #fafafa;
}
tbody tr {
transition: all, 0.2s;
}
tbody tr:hover {
background: #f5f5f5;
}
</style>

View File

@@ -1,35 +0,0 @@
<script>
import { _ } from "svelte-i18n";
export let groups;
export let handler;
let selected = "all";
</script>
<th>
<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

@@ -14,7 +14,7 @@
option.firstname + " " + (option.middlename || "") + " " + option.lastname; option.firstname + " " + (option.middlename || "") + " " + option.lastname;
const filterRunners = (label, filterText, option) => const filterRunners = (label, filterText, option) =>
label.toLowerCase().includes(filterText.toLowerCase()) || label.toLowerCase().includes(filterText.toLowerCase()) ||
option.value.id.toString().startsWith(filterText.toLowerCase()); option.value.toString().startsWith(filterText.toLowerCase());
function focus(el) { function focus(el) {
el.focus(); el.focus();
} }

View File

@@ -17,7 +17,7 @@
MeService.meControllerRemove(true) MeService.meControllerRemove(true)
.then((resp) => { .then((resp) => {
Toastify({ Toastify({
text: $_('profile-deleted'), text: "Profile deleted!",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();

View File

@@ -1,151 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick";
import { focusTrap } from "svelte-focus-trap";
import { StatsClientService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
export let modal_open;
export let new_client;
export let current_clients;
export let copy_modal_open;
function focus(el) {
el.focus();
}
$: description = "";
$: createbtnenabled = description != "";
$: processed_last_submit = true;
(() => {
document.onkeydown = (e) => {
e = e || window.event;
if (e.key === "Escape") {
modal_open = false;
}
if (e.keyCode === 13) {
if (createbtnenabled === true) {
createbtnenabled = false;
submit();
}
}
};
})();
function submit() {
if (processed_last_submit === true) {
processed_last_submit = false;
const toast = Toastify({
text: $_("statsclient-is-being-added"),
duration: -1,
}).showToast();
StatsClientService.statsClientControllerPost({description})
.then((result) => {
description = "";
modal_open = false;
//
Toastify({
text: $_("scanstation-added"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_clients.push(result);
current_clients = current_clients;
new_client = result;
copy_modal_open = true;
})
.catch((err) => {
//
})
.finally(() => {
processed_last_submit = true;
//
toast.hideToast();
});
}
}
</script>
{#if modal_open}
<div
class="fixed z-10 inset-0 overflow-y-auto"
use:focusTrap
use:clickOutside
on:click_outside={() => {
modal_open = false;
}}>
<div
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="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<div
class="inline-block align-bottom bg-white rounded-lg text-left shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<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="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
class="h-6 w-6 text-blue-600"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" />
<path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg>
</div>
<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">
{$_('create-a-new-statsclient')}
</h3>
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_('please-provide-the-required-information-to-create-a-new-statsclient')}
</p>
</div>
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6">
<label
for="description"
class="block text-sm font-medium text-gray-700">{$_('description')}</label>
<input
use:focus
autocomplete="off"
placeholder={$_('description')}
bind:value={description}
type="text"
name="description"
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 rounded-md p-2" />
</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
disabled={!createbtnenabled}
class:opacity-50={!createbtnenabled}
on:click={submit}
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">
{$_('create')}
</button>
<button
on:click={() => {
modal_open = false;
}}
type="button"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
{$_('cancel')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -1,92 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import { clickOutside } from "../base/outsideclick";
import { focusTrap } from "svelte-focus-trap";
import { ScanStationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte";
export let modal_open;
export let delete_station;
const dispatch = createEventDispatcher();
function cancelDelete() {
modal_open = false;
dispatch("cancelDelete", { id: delete_station.id });
}
function deleteClient() {
ScanStationService.donorControllerRemove(
delete_station.id,
true
)
.then((resp) => {
Toastify({
text: $_('statsclient-deleted'),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./");
})
.catch((err) => {});
}
</script>
{#if modal_open}
<div
class="fixed z-10 inset-0 overflow-y-auto"
use:focusTrap
use:clickOutside
on:click_outside={cancelDelete}>
<div
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="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<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"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<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="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 class="h-6 w-6 text-blue-600" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z"/></svg>
</div>
<!-- <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">
{$_('attention')}
</h3>
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_(
'do-you-want-to-delete-this-donor-with-all-related-donations'
)}
<br />
{$_('all-associated-scans-will-get-deleted-as-well')}
</p>
</div>
</div> -->
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
on:click={deleteClient}
type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
{$_('confirm-delete-statsclient')}
</button>
<button
on:click={cancelDelete}
type="button"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
{$_('cancel-keep-statsclient')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -1,129 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import { focusTrap } from "svelte-focus-trap";
import Toastify from "toastify-js";
import { tick, createEventDispatcher } from "svelte";
export let copy_modal_open;
export let new_client;
const dispatch = createEventDispatcher();
let valueCopy = null;
let areaDom;
let copied = false;
function close() {
copy_modal_open = false;
}
async function copy() {
valueCopy = new_client.key;
await tick();
areaDom.focus();
areaDom.select();
try {
const successful = document.execCommand("copy");
if (!successful) {
throw new Error();
}
Toastify({
text: $_("copied-token-to-clipboard"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
copied = true;
} catch (err) {
Toastify({
text: $_("error-whyile-copying-to-clipboard"),
duration: 500,
backgroundColor:
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
}).showToast();
}
// we can notifi by event or storage about copy status
valueCopy = null;
}
</script>
{#if copy_modal_open}
{#if valueCopy != null}
<textarea bind:this={areaDom}>{valueCopy}</textarea>
{/if}
<div class="fixed z-10 inset-0 overflow-y-auto" use:focusTrap>
<div
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="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<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"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<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="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
class="h-6 w-6 text-blue-600"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z" />
<path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg>
</div>
<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">
{$_('token')}
</h3>
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_('the-statsclient-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again')}
<br />
{$_('please-copy-the-token-and-store-it-somewhere-save')}
</p>
</div>
<div class="mt-2 mb-6">
<label
for="token"
class="block text-sm font-medium text-gray-700">{$_('token')}</label>
<div on:click={copy} class="inline-flex">
<p
name="token"
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">
{new_client.key}
</p>
<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">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" />
<path
fill="currentColor"
d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z" /></svg>
</div>
</div>
<p class="text-gray-500 text-xs">
{$_('click-to-copy-token-to-clipboard')}
</p>
</div>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
on:click={close}
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">
{$_('yes-i-copied-the-token')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -1,119 +0,0 @@
<script>
import { t, _ } from "svelte-i18n";
import store from "../../store";
import Toastify from "toastify-js";
import PromiseError from "../base/PromiseError.svelte";
import ConfirmStatsClientDeletion from "./ConfirmStatsClientDeletion.svelte";
import { StatsClientService } from "@odit/lfk-client-js";
let data_loaded = false;
let modal_open;
let delete_client;
export let params;
$: delete_triggered = false;
$: original_data = {};
const promise = StatsClientService.statsClientControllerGetOne(
params.clientid
).then((data) => {
data_loaded = true;
original_data = Object.assign(original_data, data);
});
function deleteClient() {
StatsClientService.statsClientControllerRemove(original_data.id, false)
.then((resp) => {
Toastify({
text: $_("statsclient-deleted"),
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
location.replace("./");
})
.catch((err) => {
modal_open = true;
delete_client = original_data;
});
}
</script>
<ConfirmStatsClientDeletion bind:modal_open bind:delete_client />
{#await promise}
{$_('loading-statsclient-details')}
{:then}
<section class="container p-5 select-none">
<div class="flex flex-row mb-4">
<div class="w-full">
<nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start">
<li class="flex items-center">
<svg
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" />
<path
d="M4 5v11h16V5H4zM2 4a1 1 0 011-1h18a1 1 0 011 1v14H2V4zM1 19h22v2H1v-2z" /></svg>
</li>
<li class="flex items-center ml-2">
<a class="mr-2" href="./">{$_('statsclient')}</a><svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-3 w-3 mr-2 stroke-current"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"><line
x1="5"
y1="12"
x2="19"
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li>
<li class="flex items-center">
<span class="mr-2">#{original_data.id}</span>
</li>
</ol>
</nav>
</div>
</div>
<div class="mb-8 text-3xl font-extrabold leading-tight">
#{original_data.id}
<span data-id="stations_actions_${original_data.id}">
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:DELETE')}
{#if delete_triggered}
<button
on:click={deleteClient}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-deletion')}</button>
<button
on:click={() => {
delete_triggered = !delete_triggered;
}}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button>
{/if}
{#if !delete_triggered}
<button
on:click={() => {
delete_triggered = true;
}}
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-statsclient')}</button>
{/if}
{/if}
</span>
</div>
<!-- -->
<div class="text-sm w-full">
<label
for="description"
class="font-medium text-gray-700">{$_('description')}</label>
<p
name="description"
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 rounded-md p-2" >
{original_data.description}</p>
</div>
</section>
{:catch error}
<PromiseError {error} />
{/await}

View File

@@ -1,33 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import store from "../../store";
import AddStatsClientModal from "./AddStatsClientModal.svelte";
import CopyStatsClientTokenModal from "./CopyStatsClientTokenModal.svelte";
import StatsClientsOverview from "./StatsClientsOverview.svelte";
export let modal_open = false;
export let copy_modal_open = false;
export let new_client = {};
let current_clients = [];
</script>
<section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight">
{$_('statsclients')}
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:CREATE')}
<button
on:click={() => {
modal_open = true;
}}
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">
{$_('create-a-new-statsclient')}
</button>
{/if}
</span>
<StatsClientsOverview bind:current_clients bind:modal_open bind:new_client bind:copy_modal_open />
</section>
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:CREATE')}
<AddStatsClientModal bind:modal_open bind:current_clients bind:new_client bind:copy_modal_open/>
<CopyStatsClientTokenModal bind:copy_modal_open bind:new_client />
{/if}

View File

@@ -1,21 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import AddStatsClientModal from "./AddStatsClientModal.svelte";
import CopyScanStationTokenModal from "./CopyStatsClientTokenModal.svelte";
import scanstations_empty from "./statsclients_empty.svg";
let modal_open = false;
let copy_modal_open = false;
let new_client = {};
let current_clients = [];
</script>
<div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500">
<img class="w-full h-44" src={scanstations_empty} alt="" />
<span class="font-bold">{$_('you-dont-have-any-scanclients-yet')}.</span><br />
<span>{$_('add-the-first-statsclient')}</span>
</p>
</div>
<AddStatsClientModal bind:modal_open bind:current_clients bind:new_client bind:copy_modal_open/>
<CopyScanStationTokenModal bind:copy_modal_open bind:new_client />

View File

@@ -1,150 +0,0 @@
<script>
import { _ } from "svelte-i18n";
import Toastify from "toastify-js";
import { StatsClientService } from "@odit/lfk-client-js";
const promise = StatsClientService.statsClientControllerGetAll().then(
(result) => {
current_clients = result;
}
);
import store from "../../store";
import StatsClientsEmptyState from "./StatsClientsEmptyState.svelte";
import ConfirmStatsClientDeletion from "./ConfirmStatsClientDeletion.svelte";
$: searchvalue = "";
$: active_deletes = [];
let delete_client = {};
let modal_open = false;
export let current_clients = [];
</script>
<ConfirmStatsClientDeletion
on:cancelDelete={(event) => {
modal_open = false;
active_deletes[event.detail.id] = false;
}}
bind:modal_open
bind:delete_client />
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT: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">{$_('statsclients-are-being-loaded')}</p>
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
</div>
{:then}
{#if current_clients.length === 0}
<StatsClientsEmptyState />
{:else}
<input
type="search"
bind:value={searchvalue}
placeholder={$_('datatable.search')}
aria-label={$_('datatable.search')}
class="gridjs-input gridjs-search-input 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>
<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">
{$_('prefix')}
</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_clients as c}
{#if Object.values(c)
.toString()
.toLowerCase()
.includes(searchvalue)}
<tr data-rowid="station_{c.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">
{c.description}
</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">
{c.prefix}
</div>
</div>
</div>
</td>
{#if active_deletes[c.id] === true}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
on:click={() => {
active_deletes[c.id] = false;
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
<button
on:click={() => {
StatsClientService.statsClientControllerRemove(c.id, false)
.then((resp) => {
current_clients = current_clients.filter((obj) => obj.id !== c.id);
Toastify({
text: $_('statsclient-deleted'),
duration: 500,
backgroundColor:
'linear-gradient(to right, #00b09b, #96c93d)',
}).showToast();
})
.catch((err) => {
modal_open = true;
delete_client = c;
});
}}
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="/statsclients/{c.id}"
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a>
{#if store.state.jwtinfo.userdetails.permissions.includes('STATSCLIENT:DELETE')}
<button
on:click={() => {
active_deletes[c.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}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 5.0 KiB

View File

@@ -43,7 +43,7 @@
if (processed_last_submit === true) { if (processed_last_submit === true) {
processed_last_submit = false; processed_last_submit = false;
const toast = Toastify({ const toast = Toastify({
text: $_('team-is-being-added'), text: "Team is being added...",
duration: -1, duration: -1,
}).showToast(); }).showToast();
RunnerTeamService.runnerTeamControllerPost({ RunnerTeamService.runnerTeamControllerPost({
@@ -55,7 +55,7 @@
modal_open = false; modal_open = false;
// //
Toastify({ Toastify({
text: $_('team-added'), text: "Team added",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();

View File

@@ -16,7 +16,7 @@
RunnerTeamService.runnerTeamControllerRemove(delete_team.id, true) RunnerTeamService.runnerTeamControllerRemove(delete_team.id, true)
.then((resp) => { .then((resp) => {
Toastify({ Toastify({
text: $_('team-deleted'), text: "Team deleted",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();

View File

@@ -26,9 +26,9 @@
export let params; export let params;
export let import_modal_open = false; export let import_modal_open = false;
$: delete_triggered = false; $: delete_triggered = false;
$: save_enabled = data_changed && teamdata.parentGroup != null; $: save_enabled = !data_changed && teamdata.parentGroup != null;
$: data_loaded = false; $: data_loaded = false;
$: data_changed = !(JSON.stringify(teamdata) === JSON.stringify(original)); $: data_changed = JSON.stringify(teamdata) === JSON.stringify(original);
$: sponsoring_contracts_show = true; $: sponsoring_contracts_show = true;
$: cards_show = true; $: cards_show = true;
$: certificates_show = true; $: certificates_show = true;
@@ -47,8 +47,6 @@
RunnerOrganizationService.runnerOrganizationControllerGetAll().then( RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
(val) => { (val) => {
orgs = val.map((r) => { orgs = val.map((r) => {
delete r.contact;
r.teams = [];
return { label: r.name, value: r }; return { label: r.name, value: r };
}); });
group = orgs.find((g) => g.value.id == teamdata.parentGroup.id); group = orgs.find((g) => g.value.id == teamdata.parentGroup.id);
@@ -69,7 +67,7 @@
RunnerTeamService.runnerTeamControllerRemove(original.id, false) RunnerTeamService.runnerTeamControllerRemove(original.id, false)
.then((resp) => { .then((resp) => {
Toastify({ Toastify({
text: $_('team-deleted'), text: "Organization deleted",
duration: 500, duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();
@@ -83,7 +81,7 @@
function submit() { function submit() {
if (data_loaded === true && save_enabled) { if (data_loaded === true && save_enabled) {
Toastify({ Toastify({
text: $_('updating-team'), text: "updating team",
duration: 2500, duration: 2500,
}).showToast(); }).showToast();
let postdata = teamdata; let postdata = teamdata;
@@ -94,7 +92,7 @@
Object.assign(original, teamdata); Object.assign(original, teamdata);
original = original; original = original;
Toastify({ Toastify({
text: $_('updated-team'), text: "updated team",
duration: 2500, duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast(); }).showToast();

View File

@@ -34,7 +34,7 @@
`[data-id="triggered_table_actions_${trackid}"]` `[data-id="triggered_table_actions_${trackid}"]`
).parentNode.parentNode.parentNode; ).parentNode.parentNode.parentNode;
Toastify({ Toastify({
text: $_('track-is-being-updated'), text: "Track is being updated...",
duration: 500, duration: 500,
}).showToast(); }).showToast();
TrackService.trackControllerPut(trackid, { TrackService.trackControllerPut(trackid, {
@@ -45,7 +45,7 @@
}) })
.then((r) => { .then((r) => {
Toastify({ Toastify({
text: $_('track-was-updated'), text: "Track was updated!",
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)", backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
duration: 1000, duration: 1000,
}).showToast(); }).showToast();

View File

@@ -27,7 +27,7 @@
}); });
function submit() { function submit() {
Toastify({ Toastify({
text: $_('updating-permissions'), text: "updating permissions...",
duration: 2500, duration: 2500,
}).showToast(); }).showToast();
to_delete.forEach((d) => { to_delete.forEach((d) => {

View File

@@ -7,10 +7,8 @@
"add-card": "Karte erstellen", "add-card": "Karte erstellen",
"add-donation": "Sponsoring erstellen", "add-donation": "Sponsoring erstellen",
"add-donor": "Sponsor:in erstellen", "add-donor": "Sponsor:in erstellen",
"add-or-update-a-payment": "Zahlung hinzufügen oder bearbeiten",
"add-scan": "Scan erstellen", "add-scan": "Scan erstellen",
"add-the-first-scanstation": "Erstelle deine erste Scannerstation.", "add-the-first-scanstation": "Erstelle deine erste Scannerstation.",
"add-the-first-statsclient": "Erstelle deinen ersten Statsclient.",
"add-user-group": "Neue Gruppe erstellen", "add-user-group": "Neue Gruppe erstellen",
"add-your-first-card": "Erstelle deine erste Läuferkarte", "add-your-first-card": "Erstelle deine erste Läuferkarte",
"add-your-first-contact": "Erstelle den ersten Kontakt", "add-your-first-contact": "Erstelle den ersten Kontakt",
@@ -24,17 +22,14 @@
"add-your-fist-donation": "Erstelle dein erstes Sponsoring", "add-your-fist-donation": "Erstelle dein erstes Sponsoring",
"add-your-fist-scan": "Füge deinen ersten Scan hinzu", "add-your-fist-scan": "Füge deinen ersten Scan hinzu",
"adding-card": "Karte wird erstellt", "adding-card": "Karte wird erstellt",
"adding-donation": "Sponsoring wird erstellt...",
"adding-scan": "Scan wird hinzugefügt", "adding-scan": "Scan wird hinzugefügt",
"address": "Adresse", "address": "Adresse",
"address-is-required": "Du musst eine Adresse angeben", "address-is-required": "Du musst eine Adresse angeben",
"after-deletion-we-cant-restore-your-old-profile": "Nach der Löschung können auch die Admins dein Profil nicht wiederherstellen!", "after-deletion-we-cant-restore-your-old-profile": "Nach der Löschung können auch die Admins dein Profil nicht wiederherstellen!",
"after-the-update-youll-get-logged-out-please-login-with-your-new-password-after-that": "Nach der Änderung wirst du abgemeldet - bitte melde dich dann mit deinem neuen Passwort an.", "after-the-update-youll-get-logged-out-please-login-with-your-new-password-after-that": "Nach der Änderung wirst du abgemeldet - bitte melde dich dann mit deinem neuen Passwort an.",
"all": "Alle",
"all-associated-donations-will-get-deleted-as-well": "Alle Sponsorings dieser Sponsor:in werden ebenfalls gelöscht", "all-associated-donations-will-get-deleted-as-well": "Alle Sponsorings dieser Sponsor:in werden ebenfalls gelöscht",
"all-associated-runners-will-be-deleted-too": "Alle zugehörigen Läufer:innen werden auch gelöscht!", "all-associated-runners-will-be-deleted-too": "Alle zugehörigen Läufer:innen werden auch gelöscht!",
"all-associated-teams-and-runners-will-be-deleted-too": "Alle assoziierten Teams und Läufer:innen werden auch gelöscht!", "all-associated-teams-and-runners-will-be-deleted-too": "Alle assoziierten Teams und Läufer:innen werden auch gelöscht!",
"already-paid": "Bereits bezahlt",
"amount": "Anzahl", "amount": "Anzahl",
"amount-per-kilometer": "Betrag pro Kilometer", "amount-per-kilometer": "Betrag pro Kilometer",
"apartment-suite-etc": "Apartment, Wohnung, etc.", "apartment-suite-etc": "Apartment, Wohnung, etc.",
@@ -49,7 +44,6 @@
"cancel-keep-donor": "Abbrechen, Sponsor:in behalten", "cancel-keep-donor": "Abbrechen, Sponsor:in behalten",
"cancel-keep-my-profile": "Abbrechen, mein Profil behalten", "cancel-keep-my-profile": "Abbrechen, mein Profil behalten",
"cancel-keep-organization": "Abbrechen und Organisation bearbeiten", "cancel-keep-organization": "Abbrechen und Organisation bearbeiten",
"cancel-keep-statsclient": "Abbrechen und Statsclient behalten",
"cancel-keep-team": "Abbrechen, Team behalten", "cancel-keep-team": "Abbrechen, Team behalten",
"cannot-reset-your-password-directly": "Schade. \nWir können das Passwort leider nicht direkt zurücksetzen.\nBitte sende uns eine Mail in der du deine Identität bestätigst.", "cannot-reset-your-password-directly": "Schade. \nWir können das Passwort leider nicht direkt zurücksetzen.\nBitte sende uns eine Mail in der du deine Identität bestätigst.",
"card-added": "Karte wurde hinzugefügt", "card-added": "Karte wurde hinzugefügt",
@@ -70,15 +64,12 @@
"confirm-delete-donor-with-all-donations": "Bestätigen, Sponsor:in mit allen Sponsorings löschen", "confirm-delete-donor-with-all-donations": "Bestätigen, Sponsor:in mit allen Sponsorings löschen",
"confirm-delete-my-user-profile": "Bestätigung, mein Benutzerprofil löschen", "confirm-delete-my-user-profile": "Bestätigung, mein Benutzerprofil löschen",
"confirm-delete-organization-and-associated-teams-runners": "Bestätugung, lösche die Organisation und alle zugehörigen Teams und Läufer:innen.", "confirm-delete-organization-and-associated-teams-runners": "Bestätugung, lösche die Organisation und alle zugehörigen Teams und Läufer:innen.",
"confirm-delete-statsclient": "Bestätigung, Statsclient löschen",
"confirm-delete-team-and-associated-runners": "Bestätigung, lösche das Team mitsamt seinen Läufer:innen.", "confirm-delete-team-and-associated-runners": "Bestätigung, lösche das Team mitsamt seinen Läufer:innen.",
"confirm-deletion": "Löschung Bestätigen", "confirm-deletion": "Löschung Bestätigen",
"confirm-the-new-password": "Neues Passwort bestätigen", "confirm-the-new-password": "Neues Passwort bestätigen",
"contact": "Kontakt", "contact": "Kontakt",
"contact-added": "Kontakt wurde hinzugefügt",
"contact-deleted": "Kontakt gelöscht", "contact-deleted": "Kontakt gelöscht",
"contact-information": "Kontaktinformation", "contact-information": "Kontaktinformation",
"contact-is-being-added": "Kontakt wird erstellt...",
"contact-is-being-updated": "Kontakt wird aktualisiert ...", "contact-is-being-updated": "Kontakt wird aktualisiert ...",
"contact-is-not-a-member-in-any-group": "Kontakt gehört zu keiner Gruppe", "contact-is-not-a-member-in-any-group": "Kontakt gehört zu keiner Gruppe",
"contacts": "Kontakte", "contacts": "Kontakte",
@@ -98,7 +89,6 @@
"create-a-new-runner": "Neue Läufer:in erstellen", "create-a-new-runner": "Neue Läufer:in erstellen",
"create-a-new-scan-fixed-only": "Neuen Scan erstellen (nur mit Festdistanz)", "create-a-new-scan-fixed-only": "Neuen Scan erstellen (nur mit Festdistanz)",
"create-a-new-scanstation": "Neue Station erstellen", "create-a-new-scanstation": "Neue Station erstellen",
"create-a-new-statsclient": "Neuen Statsclient erstellen",
"create-a-new-team": "Erstelle ein neues Team", "create-a-new-team": "Erstelle ein neues Team",
"create-a-new-track": "Neuen Track erstellen", "create-a-new-track": "Neuen Track erstellen",
"create-a-new-user": "Neue Benutzer:in anlegen", "create-a-new-user": "Neue Benutzer:in anlegen",
@@ -139,7 +129,7 @@
}, },
"delete": "Löschen", "delete": "Löschen",
"delete-contact": "Kontakt löschen", "delete-contact": "Kontakt löschen",
"delete-donation": "Sponsoring löschen", "delete-donation": "Sponsporing löschen",
"delete-donor": "Sponsor:in löschen", "delete-donor": "Sponsor:in löschen",
"delete-group": "Gruppe löschen", "delete-group": "Gruppe löschen",
"delete-organization": "Organisation löschen", "delete-organization": "Organisation löschen",
@@ -147,7 +137,6 @@
"delete-runner": "Läufer:in löschen", "delete-runner": "Läufer:in löschen",
"delete-scan": "Scan löschen", "delete-scan": "Scan löschen",
"delete-station": "Station löschen", "delete-station": "Station löschen",
"delete-statsclient": "Statsclient löschen",
"delete-team": "Team Löschen", "delete-team": "Team Löschen",
"delete-user": "Benutzer:in löschen", "delete-user": "Benutzer:in löschen",
"deleted-scan": "Scan wurde gelöscht", "deleted-scan": "Scan wurde gelöscht",
@@ -168,9 +157,6 @@
"documentation": "Dokumentation", "documentation": "Dokumentation",
"donation-amount": "Sponsoringbetrag", "donation-amount": "Sponsoringbetrag",
"donation-amount-must-be-greater-that-0-00eur": "Der Sponsoringbetrag muss größer als 0.00€ sein.", "donation-amount-must-be-greater-that-0-00eur": "Der Sponsoringbetrag muss größer als 0.00€ sein.",
"donation-deleted": "Sponsoring gelöscht",
"donation-updated": "Sponsoring wurde aktualisiert",
"donation_added": "Sponsoring hinzugefügt",
"donations": "Sponsorings", "donations": "Sponsorings",
"donor": "Sponsor:in", "donor": "Sponsor:in",
"donor-added": "Sponsor:in hinzugefügt", "donor-added": "Sponsor:in hinzugefügt",
@@ -190,7 +176,6 @@
"enabled": "aktiviert", "enabled": "aktiviert",
"enabled_large": "Aktiviert", "enabled_large": "Aktiviert",
"english": "Englisch", "english": "Englisch",
"enter-payment": "Zahlung eingeben",
"error-during-import": "Fehler beim Importieren", "error-during-import": "Fehler beim Importieren",
"error-whyile-copying-to-clipboard": "Fehler beim Kopieren in die Zwischenablage", "error-whyile-copying-to-clipboard": "Fehler beim Kopieren in die Zwischenablage",
"error_on_login": "😢Fehler beim Login", "error_on_login": "😢Fehler beim Login",
@@ -198,7 +183,6 @@
"everything-concerning-your-profile": "Alles zu deinem Profil", "everything-concerning-your-profile": "Alles zu deinem Profil",
"everything-is-more-fun-together": "Im Team macht's mehr Spaß 🏃‍♂️🏃‍♀️🏃‍♂️", "everything-is-more-fun-together": "Im Team macht's mehr Spaß 🏃‍♂️🏃‍♀️🏃‍♂️",
"faq": "FAQ", "faq": "FAQ",
"filename_sponsoringquittungsliste": "SponsoringQuittungsListe",
"filter-by-organization-team": "Filtern nach Organisation / Team", "filter-by-organization-team": "Filtern nach Organisation / Team",
"first-name": "Vorname", "first-name": "Vorname",
"first-name-is-required": "Vorname muss angegeben werden", "first-name-is-required": "Vorname muss angegeben werden",
@@ -241,7 +225,6 @@
"invalid": "Ungültig", "invalid": "Ungültig",
"invalid-mail-reset": "Das ist keine gültige E-Mail", "invalid-mail-reset": "Das ist keine gültige E-Mail",
"just-enter-how-many-you-want-and-the-system-will-create-them": "Gebe einfach ein, wie viele Blankokarten das System erstellen soll.", "just-enter-how-many-you-want-and-the-system-will-create-them": "Gebe einfach ein, wie viele Blankokarten das System erstellen soll.",
"key": "Schlüssel",
"laeufer-hinzufuegen": "Läufer:in hinzufügen", "laeufer-hinzufuegen": "Läufer:in hinzufügen",
"laeufer-importieren": "Läufer:innen importieren", "laeufer-importieren": "Läufer:innen importieren",
"laptime": "Rundenzeit", "laptime": "Rundenzeit",
@@ -285,7 +268,6 @@
"no-runners-found": "Keine Läufer:innen gefunden", "no-runners-found": "Keine Läufer:innen gefunden",
"no-tracks-added-yet": "Es wurden noch keine Tracks erstellt.", "no-tracks-added-yet": "Es wurden noch keine Tracks erstellt.",
"non-blanko": "Keine/Blankokarte", "non-blanko": "Keine/Blankokarte",
"open": "OFFEN",
"organization": "Organisation", "organization": "Organisation",
"organization-added": "Organisation hinzugefügt", "organization-added": "Organisation hinzugefügt",
"organization-deleted": "Organisation gelöscht", "organization-deleted": "Organisation gelöscht",
@@ -296,8 +278,6 @@
"organizations-are-being-loaded": "Organisationen werden geladen ...", "organizations-are-being-loaded": "Organisationen werden geladen ...",
"orgs": "Organisationen", "orgs": "Organisationen",
"oss_credit_description": "Wir verwenden eine Menge Open Source-Software bei diesen Projekten und möchten uns bei den folgenden Projekten und Mitwirkenden bedanken, die dazu beitragen, Open Source großartig zu machen!", "oss_credit_description": "Wir verwenden eine Menge Open Source-Software bei diesen Projekten und möchten uns bei den folgenden Projekten und Mitwirkenden bedanken, die dazu beitragen, Open Source großartig zu machen!",
"paid": "BEZAHLT",
"paid-amount": "Gezahlter Betrag",
"password": "Passwort", "password": "Passwort",
"password-changed": "Passwort wurde aktualisiert!", "password-changed": "Passwort wurde aktualisiert!",
"password-is-required": "Passwort muss angegeben werden", "password-is-required": "Passwort muss angegeben werden",
@@ -306,7 +286,6 @@
"password-reset-mail-sent": "Passwort-Reset Mail wurde an \"{usersEmail}\" geschickt.", "password-reset-mail-sent": "Passwort-Reset Mail wurde an \"{usersEmail}\" geschickt.",
"password-reset-successful": "Passwort erfolgreich zurückgesetzt!", "password-reset-successful": "Passwort erfolgreich zurückgesetzt!",
"passwords-dont-match": "Die Passwörter stimmen nicht überein!", "passwords-dont-match": "Die Passwörter stimmen nicht überein!",
"payment-amount-must-be-greater-than-0-00eur": "Der Zahlungsbetrag muss größer als 0.00€ sein!",
"pdf-generation-failed": "PDF Generierung fehlgeschlagen!", "pdf-generation-failed": "PDF Generierung fehlgeschlagen!",
"pdf-successfully-generated": "PDF wurde erfolgreich generiert!", "pdf-successfully-generated": "PDF wurde erfolgreich generiert!",
"pdfs-successfully-generated": "Alle PDFs wurden generiert!", "pdfs-successfully-generated": "Alle PDFs wurden generiert!",
@@ -328,14 +307,10 @@
"please-provide-the-required-information-to-add-a-new-track": "Bitte die benötigten Informationen angeben.", "please-provide-the-required-information-to-add-a-new-track": "Bitte die benötigten Informationen angeben.",
"please-provide-the-required-information-to-add-a-new-user": "Bitte gebe alle nötigen Informationen an, im die neue Benutzer:in zu erstellen.", "please-provide-the-required-information-to-add-a-new-user": "Bitte gebe alle nötigen Informationen an, im die neue Benutzer:in zu erstellen.",
"please-provide-the-required-information-to-create-a-new-scanstation": "Bitte gebe alle für eine Scannerstation notwendigen Informationen an", "please-provide-the-required-information-to-create-a-new-scanstation": "Bitte gebe alle für eine Scannerstation notwendigen Informationen an",
"please-provide-the-required-information-to-create-a-new-statsclient": "Bitte gebe alle für einen Statsclient notwendigen Informationen an",
"please-request-a-new-reset-mail": "Bitte eine neue Passwortreset-Mail anfordern...", "please-request-a-new-reset-mail": "Bitte eine neue Passwortreset-Mail anfordern...",
"please-wait-a-moment-your-login-is-still-being-processed": "Bitte warte einen Moment, deine Anmeldung wird verarbeitet",
"prefix": "Prefix",
"privacy": "Datenschutz", "privacy": "Datenschutz",
"privacy-loading": "Datenschutzerklärung lädt...", "privacy-loading": "Datenschutzerklärung lädt...",
"profile": "Profil", "profile": "Profil",
"profile-deleted": "Profil gelöscht!",
"profile-picture": "Profilbild", "profile-picture": "Profilbild",
"profile-updated": "Profil wurde aktualisiert!", "profile-updated": "Profil wurde aktualisiert!",
"read-license": "Lizenz-Text lesen", "read-license": "Lizenz-Text lesen",
@@ -379,21 +354,13 @@
"settings": "Einstellungen", "settings": "Einstellungen",
"settings-for-your-profile": "Die Einstellungen deines Accounts", "settings-for-your-profile": "Die Einstellungen deines Accounts",
"something-about-the-group": "Infos zur Gruppe", "something-about-the-group": "Infos zur Gruppe",
"sponsoring-quittungs-liste_herunterladen": "Sponsoring-Quittungs-Liste herunterladen",
"sponsorings": "Sponsoringerklaerungen", "sponsorings": "Sponsoringerklaerungen",
"stats-are-being-loaded": "Die Statistiken werden geladen...", "stats-are-being-loaded": "Die Statistiken werden geladen...",
"statsclient-deleted": "Statsclient wurde gelöscht",
"statsclient-is-being-added": "Statsclient wird angelegt...",
"statsclients": "Statsclient (aka Beamershow)",
"statsclients-are-being-loaded": "Statsclients werden geladen",
"status": "Status", "status": "Status",
"stuff-that-could-harm-your-profile": "Einstellungen, die deinem Profil nachhaltig schaden können", "stuff-that-could-harm-your-profile": "Einstellungen, die deinem Profil nachhaltig schaden können",
"successful-password-reset": "Passwort erfolgreich zurückgesetzt!", "successful-password-reset": "Passwort erfolgreich zurückgesetzt!",
"team": "Team", "team": "Team",
"team-added": "Team wurde hinzugefügt",
"team-deleted": "Team gelöscht",
"team-detail-is-being-loaded": "Team wird geladen...", "team-detail-is-being-loaded": "Team wird geladen...",
"team-is-being-added": "Team wird erstellt...",
"team-name": "Teamname", "team-name": "Teamname",
"team-name-is-required": "Teamname ist erforderlich", "team-name-is-required": "Teamname ist erforderlich",
"teams": "Teams", "teams": "Teams",
@@ -401,7 +368,6 @@
"the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number": "Die angegebene Telefonnummer ist nicht korrekt. <br /> Bitte gebe eine Telefonnummer im internationalen Format an...", "the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number": "Die angegebene Telefonnummer ist nicht korrekt. <br /> Bitte gebe eine Telefonnummer im internationalen Format an...",
"the-scans-distance-must-be-greater-than-0m": "Die Distanz muss größer als 0m sein.", "the-scans-distance-must-be-greater-than-0m": "Die Distanz muss größer als 0m sein.",
"the-scanstations-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again": "Der Scannerstation Token wird nur einmal angezeigt - du kannst ihn nicht ändern oder ihn dir nochmal anzeigen lassen!", "the-scanstations-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again": "Der Scannerstation Token wird nur einmal angezeigt - du kannst ihn nicht ändern oder ihn dir nochmal anzeigen lassen!",
"the-statsclient-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again": "Der Statsclient Token wird nur einmal angezeigt - du kannst ihn nicht ändern oder ihn dir nochmal anzeigen lassen!",
"there-are-no-cards-yet": "Es gibt noch keine Läuferkarten.", "there-are-no-cards-yet": "Es gibt noch keine Läuferkarten.",
"there-are-no-contacts-added-yet": "Es wurden noch keine Kontakte hinzugefügt.", "there-are-no-contacts-added-yet": "Es wurden noch keine Kontakte hinzugefügt.",
"there-are-no-donations-yet": "Es gibt noch keine Sponsorings", "there-are-no-donations-yet": "Es gibt noch keine Sponsorings",
@@ -419,35 +385,26 @@
"total-distance": "gelaufene Strecke", "total-distance": "gelaufene Strecke",
"total-donation-amount": "Gesamtbetrag", "total-donation-amount": "Gesamtbetrag",
"total-donations": "Spendensumme", "total-donations": "Spendensumme",
"total-paid-amount": "Gezahlter Gesamtbetrag",
"total-scans": "gesamte Scans", "total-scans": "gesamte Scans",
"total_donation_amount_in_eur": "Gesamtbetrag in €",
"track": "Track", "track": "Track",
"track-added": "Track hinzugefügt", "track-added": "Track hinzugefügt",
"track-data-is-being-loaded": "Trackdaten werden geladen", "track-data-is-being-loaded": "Trackdaten werden geladen",
"track-is-being-added": "Track wird hinzugefügt...", "track-is-being-added": "Track wird hinzugefügt...",
"track-is-being-updated": "Track wird aktualisiert...",
"track-length-in-m": "Tracklänge (in Metern)", "track-length-in-m": "Tracklänge (in Metern)",
"track-length-must-be-greater-than-0": "Die Länge muss größer als 0 (Meter) sein", "track-length-must-be-greater-than-0": "Die Länge muss größer als 0 (Meter) sein",
"track-name": "Trackname", "track-name": "Trackname",
"track-name-must-not-be-empty": "Der Name muss angegeben werden", "track-name-must-not-be-empty": "Der Name muss angegeben werden",
"track-was-updated": "Track wurde aktualisiert",
"tracks": "Tracks", "tracks": "Tracks",
"unpaid": "Offen",
"update-card": "Karte aktualisieren",
"update-password": "Passwort ändern", "update-password": "Passwort ändern",
"updated-contact": "Kontakt aktualisiert!", "updated-contact": "Kontakt aktualisiert!",
"updated-donor": "Sponsor:in wurde aktualisiert", "updated-donor": "Sponsor:in wurde aktualisiert",
"updated-organization": "Organisation wurde aktualisiert", "updated-organization": "Organisation wurde aktualisiert",
"updated-scan": "Scan wurde aktualisiert", "updated-scan": "Scan wurde aktualisiert",
"updated-team": "Team wurde aktualisiert",
"updateing-group": "Gruppe wird aktualisiert...", "updateing-group": "Gruppe wird aktualisiert...",
"updating-card": "Karte wird aktualisiert", "updating-card": "Karte wird aktualisiert",
"updating-donation": "Sponsoring wird aktualisiert",
"updating-organization": "Organisation wird aktualisiert", "updating-organization": "Organisation wird aktualisiert",
"updating-permissions": "Berechtigungen werden aktualisiert...", "updating-permissions": "Berechtigungen werden aktualisiert...",
"updating-runner": "Läufer:in wird aktualisiert.", "updating-runner": "Läufer:in wird aktualisiert.",
"updating-team": "Team wird aktualisiert",
"updating-user": "Benutzer:in wird aktualisiert...", "updating-user": "Benutzer:in wird aktualisiert...",
"updating-your-profile": "Profil wird aktualisiert...", "updating-your-profile": "Profil wird aktualisiert...",
"user-added": "Benutzer hinzugefügt", "user-added": "Benutzer hinzugefügt",
@@ -465,10 +422,8 @@
"welcome_wavinghand": "Willkommen 👋", "welcome_wavinghand": "Willkommen 👋",
"yes-i-copied-the-token": "Ja, ich habe den Token kopiert", "yes-i-copied-the-token": "Ja, ich habe den Token kopiert",
"you-are-going-to-loose-all-permissions-and-access-to-the-runner-system": "Du wirst all deine Berechtigungen und den Zugriff aufs Läufersystem verlieren!", "you-are-going-to-loose-all-permissions-and-access-to-the-runner-system": "Du wirst all deine Berechtigungen und den Zugriff aufs Läufersystem verlieren!",
"you-can-enter-the-donations-paid-amount-manually-or-use-the-max-button-to-use-the-donations-exact-amount": "Du kannst den Betrag der Zahlung entweder manuell eingeben oder über den MAX Button auf den Spendenbetrag setzen",
"you-can-now-use-your-new-password-to-log-in-to-your-account": "Du kannst dich jetzt mit deinem neuen Passwort anmelden! 🎉", "you-can-now-use-your-new-password-to-log-in-to-your-account": "Du kannst dich jetzt mit deinem neuen Passwort anmelden! 🎉",
"you-can-provide-a-runner-but-you-dont-have-to": "Du kannst eine Läufer:in angeben, musst aber nicht.", "you-can-provide-a-runner-but-you-dont-have-to": "Du kannst eine Läufer:in angeben, musst aber nicht.",
"you-dont-have-any-scanclients-yet": "Es gibt noch keine Statsclients",
"you-dont-have-any-scanstations-yet": "Es gibt noch keine Scannerstationen", "you-dont-have-any-scanstations-yet": "Es gibt noch keine Scannerstationen",
"you-have-to-provide-an-organization": "Du musst eine Organisation angeben", "you-have-to-provide-an-organization": "Du musst eine Organisation angeben",
"you-have-to-save-your-changes-to-generate-a-link": "Du musst deine Änderungen speichern, um einen Link zu generieren.", "you-have-to-save-your-changes-to-generate-a-link": "Du musst deine Änderungen speichern, um einen Link zu generieren.",

View File

@@ -6,11 +6,9 @@
"active": "Active", "active": "Active",
"add-card": "Add Card", "add-card": "Add Card",
"add-donation": "Add donation", "add-donation": "Add donation",
"add-donor": "Add donor", "add-donor": "add donor",
"add-or-update-a-payment": "Add or update a payment",
"add-scan": "Add scan", "add-scan": "Add scan",
"add-the-first-scanstation": "Add your first scanstation.", "add-the-first-scanstation": "Add your first scanstation.",
"add-the-first-statsclient": "Add your first statsclient.",
"add-user-group": "Add User Group", "add-user-group": "Add User Group",
"add-your-first-card": "Add your first card", "add-your-first-card": "Add your first card",
"add-your-first-contact": "Add your first contact", "add-your-first-contact": "Add your first contact",
@@ -24,17 +22,14 @@
"add-your-fist-donation": "Add your fist donation", "add-your-fist-donation": "Add your fist donation",
"add-your-fist-scan": "Add your fist scan", "add-your-fist-scan": "Add your fist scan",
"adding-card": "Adding Card", "adding-card": "Adding Card",
"adding-donation": "Adding donation...",
"adding-scan": "Adding Scan", "adding-scan": "Adding Scan",
"address": "Address", "address": "Address",
"address-is-required": "Address is required", "address-is-required": "Address is required",
"after-deletion-we-cant-restore-your-old-profile": "After deletion we can't restore your old profile!", "after-deletion-we-cant-restore-your-old-profile": "After deletion we can't restore your old profile!",
"after-the-update-youll-get-logged-out-please-login-with-your-new-password-after-that": "After the update you'll get logged out - Please login with your new password after that.", "after-the-update-youll-get-logged-out-please-login-with-your-new-password-after-that": "After the update you'll get logged out - Please login with your new password after that.",
"all": "all",
"all-associated-donations-will-get-deleted-as-well": "All associated donations will get deleted as well", "all-associated-donations-will-get-deleted-as-well": "All associated donations will get deleted as well",
"all-associated-runners-will-be-deleted-too": "All associated runners will be deleted too!", "all-associated-runners-will-be-deleted-too": "All associated 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 associated teams and runners will be deleted too!",
"already-paid": "Already paid",
"amount": "Amount", "amount": "Amount",
"amount-per-kilometer": "Amount per kilometer", "amount-per-kilometer": "Amount per kilometer",
"apartment-suite-etc": "Apartment, suite, etc.", "apartment-suite-etc": "Apartment, suite, etc.",
@@ -49,7 +44,6 @@
"cancel-keep-donor": "Cancel, keep donor", "cancel-keep-donor": "Cancel, keep donor",
"cancel-keep-my-profile": "Cancel, keep my profile", "cancel-keep-my-profile": "Cancel, keep my profile",
"cancel-keep-organization": "Cancel, keep organization", "cancel-keep-organization": "Cancel, keep organization",
"cancel-keep-statsclient": "Cancel and keep statsclient",
"cancel-keep-team": "Cancel, keep team", "cancel-keep-team": "Cancel, keep team",
"cannot-reset-your-password-directly": "Bummer. We unfortunately cannot reset your password directly. Please send us a mail and confirm your identity", "cannot-reset-your-password-directly": "Bummer. We unfortunately cannot reset your password directly. Please send us a mail and confirm your identity",
"card-added": "Card added", "card-added": "Card added",
@@ -70,15 +64,12 @@
"confirm-delete-donor-with-all-donations": "Confirm, delete donor with all donations", "confirm-delete-donor-with-all-donations": "Confirm, delete donor with all donations",
"confirm-delete-my-user-profile": "Confirm, delete my user profile", "confirm-delete-my-user-profile": "Confirm, delete my user profile",
"confirm-delete-organization-and-associated-teams-runners": "Confirm, delete organization and associated teams+runners.", "confirm-delete-organization-and-associated-teams-runners": "Confirm, delete organization and associated teams+runners.",
"confirm-delete-statsclient": "Confirm, delete statsclient",
"confirm-delete-team-and-associated-runners": "Confirm, delete team and associated runners.", "confirm-delete-team-and-associated-runners": "Confirm, delete team and associated runners.",
"confirm-deletion": "Confirm Deletion", "confirm-deletion": "Confirm Deletion",
"confirm-the-new-password": "Confirm the new password", "confirm-the-new-password": "Confirm the new password",
"contact": "Contact", "contact": "Contact",
"contact-added": "Contact added",
"contact-deleted": "Contact deleted", "contact-deleted": "Contact deleted",
"contact-information": "Contact Information", "contact-information": "Contact Information",
"contact-is-being-added": "Contact is being added...",
"contact-is-being-updated": "Contact is being updated...", "contact-is-being-updated": "Contact is being updated...",
"contact-is-not-a-member-in-any-group": "Contact is not a member in any group", "contact-is-not-a-member-in-any-group": "Contact is not a member in any group",
"contacts": "Contacts", "contacts": "Contacts",
@@ -98,7 +89,6 @@
"create-a-new-runner": "Create a new Runner", "create-a-new-runner": "Create a new Runner",
"create-a-new-scan-fixed-only": "Create a new scan (fixed only)", "create-a-new-scan-fixed-only": "Create a new scan (fixed only)",
"create-a-new-scanstation": "Create a new station", "create-a-new-scanstation": "Create a new station",
"create-a-new-statsclient": "Create a new statsclient",
"create-a-new-team": "Create a new team", "create-a-new-team": "Create a new team",
"create-a-new-track": "Create a new Track", "create-a-new-track": "Create a new Track",
"create-a-new-user": "Create a new User", "create-a-new-user": "Create a new User",
@@ -147,7 +137,6 @@
"delete-runner": "Delete Runner", "delete-runner": "Delete Runner",
"delete-scan": "Delete scan", "delete-scan": "Delete scan",
"delete-station": "Delete station", "delete-station": "Delete station",
"delete-statsclient": "Delete statsclient",
"delete-team": "Delete Team", "delete-team": "Delete Team",
"delete-user": "Delete User", "delete-user": "Delete User",
"deleted-scan": "Deleted scan", "deleted-scan": "Deleted scan",
@@ -168,9 +157,6 @@
"documentation": "Documentation", "documentation": "Documentation",
"donation-amount": "Donation amount", "donation-amount": "Donation amount",
"donation-amount-must-be-greater-that-0-00eur": "Donation amount must be greater that 0.00€", "donation-amount-must-be-greater-that-0-00eur": "Donation amount must be greater that 0.00€",
"donation-deleted": "Donation deleted",
"donation-updated": "Donation updated",
"donation_added": "Donation_added",
"donations": "Donations", "donations": "Donations",
"donor": "Donor", "donor": "Donor",
"donor-added": "Donor added", "donor-added": "Donor added",
@@ -190,7 +176,6 @@
"enabled": "enabled", "enabled": "enabled",
"enabled_large": "Enabled", "enabled_large": "Enabled",
"english": "English", "english": "English",
"enter-payment": "Enter payment",
"error-during-import": "Error during import", "error-during-import": "Error during import",
"error-whyile-copying-to-clipboard": "Error while copying to clipboard", "error-whyile-copying-to-clipboard": "Error while copying to clipboard",
"error_on_login": "Error on login", "error_on_login": "Error on login",
@@ -198,7 +183,6 @@
"everything-concerning-your-profile": "Everything concerning your profile", "everything-concerning-your-profile": "Everything concerning your profile",
"everything-is-more-fun-together": "everything is more fun together 🏃‍♂️🏃‍♀️🏃‍♂️", "everything-is-more-fun-together": "everything is more fun together 🏃‍♂️🏃‍♀️🏃‍♂️",
"faq": "FAQ", "faq": "FAQ",
"filename_sponsoringquittungsliste": "DonorReceiptList",
"filter-by-organization-team": "Filter by Organization/ Team", "filter-by-organization-team": "Filter by Organization/ Team",
"first-name": "First name", "first-name": "First name",
"first-name-is-required": "First Name is required", "first-name-is-required": "First Name is required",
@@ -241,7 +225,6 @@
"invalid": "Invalid", "invalid": "Invalid",
"invalid-mail-reset": "the provided email is invalid", "invalid-mail-reset": "the provided email is invalid",
"just-enter-how-many-you-want-and-the-system-will-create-them": "Just enter how many you want and the system will create them", "just-enter-how-many-you-want-and-the-system-will-create-them": "Just enter how many you want and the system will create them",
"key": "Key",
"laeufer-hinzufuegen": "Add runner", "laeufer-hinzufuegen": "Add runner",
"laeufer-importieren": "Läufer importieren", "laeufer-importieren": "Läufer importieren",
"laptime": "Laptime", "laptime": "Laptime",
@@ -285,7 +268,6 @@
"no-runners-found": "No runners found", "no-runners-found": "No runners found",
"no-tracks-added-yet": "there are no tracks added yet.", "no-tracks-added-yet": "there are no tracks added yet.",
"non-blanko": "Non/Blanko", "non-blanko": "Non/Blanko",
"open": "OPEN",
"organization": "Organization", "organization": "Organization",
"organization-added": "Organization added", "organization-added": "Organization added",
"organization-deleted": "Organization deleted", "organization-deleted": "Organization deleted",
@@ -296,8 +278,6 @@
"organizations-are-being-loaded": "organizations are being loaded...", "organizations-are-being-loaded": "organizations are being loaded...",
"orgs": "Organizations", "orgs": "Organizations",
"oss_credit_description": "We use a lot of open source software on these projects, and would like to thank the following projects and contributors who help make open source great!", "oss_credit_description": "We use a lot of open source software on these projects, and would like to thank the following projects and contributors who help make open source great!",
"paid": "PAID",
"paid-amount": "Paid amount",
"password": "Password", "password": "Password",
"password-changed": "Password changed!", "password-changed": "Password changed!",
"password-is-required": "Password is required", "password-is-required": "Password is required",
@@ -306,7 +286,6 @@
"password-reset-mail-sent": "Password reset mail was sent to \"{usersEmail}\".", "password-reset-mail-sent": "Password reset mail was sent to \"{usersEmail}\".",
"password-reset-successful": "Password Reset successful!", "password-reset-successful": "Password Reset successful!",
"passwords-dont-match": "Passwords don't match!", "passwords-dont-match": "Passwords don't match!",
"payment-amount-must-be-greater-than-0-00eur": "Payment amount must be greater than 0.00€!",
"pdf-generation-failed": "PDF generation failed!", "pdf-generation-failed": "PDF generation failed!",
"pdf-successfully-generated": "PDF successfully generated!", "pdf-successfully-generated": "PDF successfully generated!",
"pdfs-successfully-generated": "PDFs successfully generated!", "pdfs-successfully-generated": "PDFs successfully generated!",
@@ -328,14 +307,10 @@
"please-provide-the-required-information-to-add-a-new-track": "Please provide the required information to add a new track.", "please-provide-the-required-information-to-add-a-new-track": "Please provide the required information to add a new track.",
"please-provide-the-required-information-to-add-a-new-user": "Please provide the required information to add a new user.", "please-provide-the-required-information-to-add-a-new-user": "Please provide the required information to add a new user.",
"please-provide-the-required-information-to-create-a-new-scanstation": "Please provide the required information to create a new scanstation", "please-provide-the-required-information-to-create-a-new-scanstation": "Please provide the required information to create a new scanstation",
"please-provide-the-required-information-to-create-a-new-statsclient": "Please provide the required information to create a new statsclient",
"please-request-a-new-reset-mail": "Please request a new reset mail...", "please-request-a-new-reset-mail": "Please request a new reset mail...",
"please-wait-a-moment-your-login-is-still-being-processed": "Please wait a moment, your login is still being processed",
"prefix": "Prefix",
"privacy": "Privacy", "privacy": "Privacy",
"privacy-loading": "Privacy loading...", "privacy-loading": "Privacy loading...",
"profile": "Profile", "profile": "Profile",
"profile-deleted": "Profile deleted!",
"profile-picture": "Profile Picture", "profile-picture": "Profile Picture",
"profile-updated": "Profile updated!", "profile-updated": "Profile updated!",
"read-license": "Read License", "read-license": "Read License",
@@ -379,21 +354,13 @@
"settings": "Settings", "settings": "Settings",
"settings-for-your-profile": "Settings for your profile", "settings-for-your-profile": "Settings for your profile",
"something-about-the-group": "Something about the group...", "something-about-the-group": "Something about the group...",
"sponsoring-quittungs-liste_herunterladen": "Download donor receipt list",
"sponsorings": "Sponsorings", "sponsorings": "Sponsorings",
"stats-are-being-loaded": "stats are being loaded...", "stats-are-being-loaded": "stats are being loaded...",
"statsclient-deleted": "Deleted statsclient",
"statsclient-is-being-added": "Statsclient is being added...",
"statsclients": "Statsclients (aka Beamershow)",
"statsclients-are-being-loaded": "Loading statsclients",
"status": "Status", "status": "Status",
"stuff-that-could-harm-your-profile": "Stuff that could harm your profile", "stuff-that-could-harm-your-profile": "Stuff that could harm your profile",
"successful-password-reset": "Successful password reset!", "successful-password-reset": "Successful password reset!",
"team": "Team", "team": "Team",
"team-added": "Team added",
"team-deleted": "Team deleted",
"team-detail-is-being-loaded": "team detail is being loaded...", "team-detail-is-being-loaded": "team detail is being loaded...",
"team-is-being-added": "Team is being added...",
"team-name": "Team name", "team-name": "Team name",
"team-name-is-required": "team name is required", "team-name-is-required": "team name is required",
"teams": "Teams", "teams": "Teams",
@@ -401,7 +368,6 @@
"the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number": "the provided phone number is invalid.<br />please enter a valid international number...", "the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number": "the provided phone number is invalid.<br />please enter a valid international number...",
"the-scans-distance-must-be-greater-than-0m": "The scan's distance must be greater than 0m", "the-scans-distance-must-be-greater-than-0m": "The scan's distance must be greater than 0m",
"the-scanstations-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again": "The scanstation api token will only get displayed once - you won't 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": "The scanstation api token will only get displayed once - you won't be able to change or view it again!",
"the-statsclient-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again": "The statsclient api token will only get displayed once - you won't be able to change or view it again!",
"there-are-no-cards-yet": "There are no cards yet.", "there-are-no-cards-yet": "There are no cards yet.",
"there-are-no-contacts-added-yet": "There are no contacts added yet.", "there-are-no-contacts-added-yet": "There are no contacts added yet.",
"there-are-no-donations-yet": "There are no donations yet", "there-are-no-donations-yet": "There are no donations yet",
@@ -419,35 +385,27 @@
"total-distance": "total distance", "total-distance": "total distance",
"total-donation-amount": "total donation amount", "total-donation-amount": "total donation amount",
"total-donations": "total donations", "total-donations": "total donations",
"total-paid-amount": "Total paid amount",
"total-scans": "total scans", "total-scans": "total scans",
"total_donation_amount_in_eur": "Total donation amount in €",
"track": "Track", "track": "Track",
"track-added": "Track added", "track-added": "Track added",
"track-data-is-being-loaded": "Track data is being loaded", "track-data-is-being-loaded": "Track data is being loaded",
"track-is-being-added": "Track is being added...", "track-is-being-added": "Track is being added...",
"track-is-being-updated": "Track is being updated...",
"track-length-in-m": "Track Length in m", "track-length-in-m": "Track Length in m",
"track-length-must-be-greater-than-0": "Track length must be greater than 0", "track-length-must-be-greater-than-0": "Track length must be greater than 0",
"track-name": "Track name", "track-name": "Track name",
"track-name-must-not-be-empty": "Track name must not be empty", "track-name-must-not-be-empty": "Track name must not be empty",
"track-was-updated": "Track was updated!",
"tracks": "Tracks", "tracks": "Tracks",
"unpaid": "Unpaid",
"update-card": "Update Card", "update-card": "Update Card",
"update-password": "Update password", "update-password": "Update password",
"updated-contact": "Updated contact!", "updated-contact": "Updated contact!",
"updated-donor": "updated donor", "updated-donor": "updated donor",
"updated-organization": "updated organization", "updated-organization": "updated organization",
"updated-scan": "updated scan", "updated-scan": "updated scan",
"updated-team": "Updated team",
"updateing-group": "updateing group...", "updateing-group": "updateing group...",
"updating-card": "Updating card", "updating-card": "Updating card",
"updating-donation": "Updating donation",
"updating-organization": "updating organization", "updating-organization": "updating organization",
"updating-permissions": "updating permissions...", "updating-permissions": "updating permissions...",
"updating-runner": "Updating runner...", "updating-runner": "Updating runner...",
"updating-team": "Updating team",
"updating-user": "updating user...", "updating-user": "updating user...",
"updating-your-profile": "Updating your profile...", "updating-your-profile": "Updating your profile...",
"user-added": "User added", "user-added": "User added",
@@ -465,10 +423,8 @@
"welcome_wavinghand": "Welcome 👋", "welcome_wavinghand": "Welcome 👋",
"yes-i-copied-the-token": "Yes, I copied the token", "yes-i-copied-the-token": "Yes, I copied the token",
"you-are-going-to-loose-all-permissions-and-access-to-the-runner-system": "You are going to loose all permissions and access to the runner system!", "you-are-going-to-loose-all-permissions-and-access-to-the-runner-system": "You are going to loose all permissions and access to the runner system!",
"you-can-enter-the-donations-paid-amount-manually-or-use-the-max-button-to-use-the-donations-exact-amount": "You can enter the donation's paid amount manually or use the MAX button to use the donation's exact amount.",
"you-can-now-use-your-new-password-to-log-in-to-your-account": "You can now use your new password to log in to your account! 🎉", "you-can-now-use-your-new-password-to-log-in-to-your-account": "You can now use your new password to log in to your account! 🎉",
"you-can-provide-a-runner-but-you-dont-have-to": "You can provide a runner, but you don't have to.", "you-can-provide-a-runner-but-you-dont-have-to": "You can provide a runner, but you don't have to.",
"you-dont-have-any-scanclients-yet": "You don't have any statsclients yet",
"you-dont-have-any-scanstations-yet": "You don't have any scanstations yet", "you-dont-have-any-scanstations-yet": "You don't have any scanstations yet",
"you-have-to-provide-an-organization": "You have to provide an organization", "you-have-to-provide-an-organization": "You have to provide an organization",
"you-have-to-save-your-changes-to-generate-a-link": "You have to save your changes to generate a link.", "you-have-to-save-your-changes-to-generate-a-link": "You have to save your changes to generate a link.",

View File

@@ -1,4 +1,4 @@
import './style.css'; import 'windi.css';
import "toastify-js/src/toastify.css"; import "toastify-js/src/toastify.css";
import "gridjs/dist/theme/mermaid.css"; import "gridjs/dist/theme/mermaid.css";
import App from './App.svelte'; import App from './App.svelte';

View File

@@ -1,3 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -1,6 +1,4 @@
module.exports = { module.exports = {
mode: 'jit',
purge: [ './src/**/*.svelte' ],
theme: { theme: {
extend: { extend: {
colors: { colors: {

View File

@@ -1,4 +1,5 @@
import svelte from '@sveltejs/vite-plugin-svelte'; import svelte from '@sveltejs/vite-plugin-svelte';
import windiCSS from 'vite-plugin-windicss';
import { minify } from 'html-minifier'; import { minify } from 'html-minifier';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
// //
@@ -20,15 +21,25 @@ export default defineConfig(({ command, mode }) => {
build: { build: {
polyfillDynamicImport: false, polyfillDynamicImport: false,
cssCodeSplit: false, cssCodeSplit: false,
minify: isProduction, minify: isProduction
target: ["es2020", "esnext", "edge88", "chrome87", "safari14"]
}, },
plugins: [ plugins: [
windiCSS({
//@ts-ignore
verbose: true,
silent: false,
debug: true,
config: 'tailwind.config.js', // tailwind config file path (optional)
compile: false, // false: interpretation mode; true: compilation mode
prefix: 'windi-', // set compilation mode style prefix
globalPreflight: true, // set preflight style is global or scoped
globalUtility: true // set utility style is global or scoped
}),
svelte({ svelte({
//@ts-ignore //@ts-ignore
hot: !isProduction, hot: !isProduction,
emitCss: true, emitCss: true,
extensions: ['.md', '.svx', '.svelte'], extensions: [ '.md', '.svx', '.svelte' ],
preprocess: [ preprocess: [
// //
] ]