Compare commits
265 Commits
feature/14
...
1.3.2
Author | SHA1 | Date | |
---|---|---|---|
39bc6c4945
|
|||
b94e3b745f
|
|||
6f337aeee1
|
|||
5d48060834
|
|||
c842c203e2
|
|||
5bcfc8db75
|
|||
27b4dde755
|
|||
91ab199769
|
|||
e75be49be4
|
|||
505fb8cb08
|
|||
e5c9265588
|
|||
02003ec80e
|
|||
133470b6f2
|
|||
4a6230c439
|
|||
fdc7d80bbf
|
|||
352551e168
|
|||
7aec050419
|
|||
4289034436 | |||
8f8858f100
|
|||
d98fb0d5b2
|
|||
5014bf5bc5
|
|||
0708cabc75
|
|||
4fcb26cf93
|
|||
269def20d1
|
|||
b8de9e0e42
|
|||
7b2b598588
|
|||
e0b61486b0
|
|||
3f86f7412f
|
|||
6454d960de
|
|||
37154c188b
|
|||
8da7578a0a
|
|||
2a64094006 | |||
e9ce9644ff | |||
52439aa5bc
|
|||
ccf865687b
|
|||
cac34db1fd
|
|||
faf3893180
|
|||
c33dfcfddd
|
|||
019e14ab1f
|
|||
b5790196c6
|
|||
94a64ca690
|
|||
a6ce04c903 | |||
165c154233 | |||
318547db46
|
|||
e60c09e19c
|
|||
4834d1484c
|
|||
4b6342727e
|
|||
cb5fa52cd9
|
|||
947d01cf7f
|
|||
3563394fb3
|
|||
269d7a7def
|
|||
e95f2333b0
|
|||
950217e0a3
|
|||
5e65fb3301
|
|||
2a294cde04
|
|||
e95420d79c
|
|||
cffbd17dc7
|
|||
00de8c3d75
|
|||
1f4711d07a
|
|||
30e3396897
|
|||
5291e049a1
|
|||
08fbb504c9
|
|||
e9ca1d3e5d
|
|||
eb80406fdb
|
|||
9fe53b0b9c
|
|||
9ae5e62e5d
|
|||
1613ae7de6
|
|||
ed1caa7be7 | |||
d88f3a5a27 | |||
c98eb49ae3
|
|||
6d9d8a4724
|
|||
7d8c68a455
|
|||
8f33640bec
|
|||
f89023e24a
|
|||
f5d14f2e18
|
|||
9811ede3b2
|
|||
b2e51fea48
|
|||
2a915620c9
|
|||
526688935f
|
|||
c7dcf7c66d
|
|||
06411dc147
|
|||
195d182cc9
|
|||
17217dae76
|
|||
f105cc0a41
|
|||
2f62c7ae89
|
|||
f6c1fea17c
|
|||
178dc93319
|
|||
8ffe8eff06
|
|||
bd4952ee57
|
|||
4b171fd04f
|
|||
2c198cfde8
|
|||
7c6d39b5fa
|
|||
9c13b2f9e9
|
|||
1ec9556aa6
|
|||
adec38b50b
|
|||
a35af6f020
|
|||
e74ff5e885
|
|||
c87561f63b
|
|||
c681570134
|
|||
53b945c72f
|
|||
f6985daec7
|
|||
5662c3b6da
|
|||
9def0b27c9
|
|||
52a02c82d2
|
|||
c241961d0a
|
|||
8f50555a06
|
|||
b35375c929
|
|||
bf1e715261
|
|||
0240e1dca2
|
|||
7cec2a00c5
|
|||
239f79fecb
|
|||
0265a59b82
|
|||
57dce34fc5
|
|||
d8110580e9
|
|||
03b7ada5ef
|
|||
1df505ea00
|
|||
3e8dac3203
|
|||
95707a71a9
|
|||
fc2c2907c4
|
|||
ebdd1c2c0c
|
|||
13254b24dd
|
|||
e17eb64031
|
|||
a0727a0291
|
|||
f7f7926829
|
|||
9b7dca341b
|
|||
da3300562a
|
|||
e2ddb5a14c
|
|||
fb9645aed6
|
|||
a4ebc7e126
|
|||
fd5db7d68a
|
|||
e7eddb4f08
|
|||
94155845f0
|
|||
3abf608b15
|
|||
d31fe2363b
|
|||
11a56f87e8
|
|||
19793cdcd4
|
|||
9363773fa1
|
|||
c7990882cf
|
|||
d4ab76ea1b
|
|||
2c992a0e63
|
|||
88f96acc3c
|
|||
245db06173
|
|||
49c2cd5c4b
|
|||
64db553185
|
|||
a06a19ce9c
|
|||
592ddc1541
|
|||
cb5f2b73d0
|
|||
2304b12c1c
|
|||
38d3e1912c
|
|||
fbc14fd7b4
|
|||
0283df22c8
|
|||
845737ee8e
|
|||
6993511c67
|
|||
9111ad147c
|
|||
333214aa8f
|
|||
c0cde02fec
|
|||
070a20a2e5
|
|||
c5e8409079
|
|||
67eff0eda9
|
|||
3de7b632e0
|
|||
842248e4c4
|
|||
c5d7ec25b5
|
|||
a9a965d698
|
|||
dc866dd540
|
|||
89252619b1
|
|||
2699b06d7c
|
|||
fd0d45f721
|
|||
5ecf838dd2
|
|||
45a7a90cb8
|
|||
cac851f2b1
|
|||
238082b657
|
|||
aecbabe522
|
|||
a9cdac4f74
|
|||
a59dbbe50e
|
|||
9bec95ede8
|
|||
70307a9e82
|
|||
ef077b4e6a
|
|||
dcabed4e93
|
|||
1af047f66e
|
|||
ee91748b3c
|
|||
e5241d619b
|
|||
d79608edbb
|
|||
4cbd26580e
|
|||
fe62ad5539
|
|||
eb13f038a1
|
|||
9505c2b030 | |||
008835c24f | |||
7083b3d8d2 | |||
754931b2f6 | |||
2dc8ffba32 | |||
d0fe6a2e85 | |||
85705b6e68 | |||
3ea7a015a9 | |||
44329413ed | |||
46db68ab22 | |||
dc9d7f22a2 | |||
f917018fd9 | |||
7b420c430d | |||
00359d25c1 | |||
d8a3063735 | |||
6491af19e3
|
|||
61328d20ed | |||
0a6d92a1f3
|
|||
3a576d1073
|
|||
b30b98b521
|
|||
43d82a2af0
|
|||
6a4495b813 | |||
e8a0ad6647
|
|||
92b89cc4d8
|
|||
268b1b1d98
|
|||
75bc89ca30
|
|||
0625937068 | |||
32a9074963
|
|||
b869b5fd2a
|
|||
3a3e2f7157 | |||
bea57aa03a | |||
30991d5364 | |||
5cc8b0811c | |||
2c73b9862d | |||
732b2f061e | |||
3680533eef | |||
1307d72c9d | |||
405dfa0c34 | |||
5c2d154ad1 | |||
f2bf8d9bac | |||
f9cfd6bd06
|
|||
287f63fa52
|
|||
5fe47634e8
|
|||
a6590910cf | |||
ad454c386c | |||
0b2c296de0
|
|||
0e85940cba
|
|||
8d479c32f8 | |||
549785cf7d | |||
aafc4c8d62
|
|||
47dedbdc73
|
|||
6fe134afc8
|
|||
63a50f92e7
|
|||
ca6da15ef7
|
|||
8dfa19fa0f
|
|||
0feee0ae2f
|
|||
2a6a39916a | |||
f0a2b2859f
|
|||
32ddb66fc8
|
|||
df63c2388d
|
|||
757655ea63
|
|||
329c1cc037 | |||
da6dd55d13 | |||
0e5490f1c8 | |||
b82d638de1 | |||
224034dcc6 | |||
026d3d41c1 | |||
fd1a06b359 | |||
452d010183
|
|||
eb1c17e3ac
|
|||
a101873eb0
|
|||
3d2acb692a
|
|||
0900c2691e
|
|||
1337676e08
|
|||
2e075eafab | |||
14d64b6070 | |||
81b8fbf4e3 | |||
24d074752f | |||
08047a9307 | |||
1b0cd5b90b |
@@ -1 +1,2 @@
|
||||
public/env.sample.js
|
||||
public/env.sample.js
|
||||
.pnpm-store
|
29
.drone.yml
29
.drone.yml
@@ -19,6 +19,13 @@ get:
|
||||
path: odit-git-bot
|
||||
name: sshkey
|
||||
|
||||
---
|
||||
kind: secret
|
||||
name: npm_url
|
||||
get:
|
||||
path: odit-npm-cache
|
||||
name: url
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: kubernetes
|
||||
@@ -27,10 +34,14 @@ name: build:dev
|
||||
steps:
|
||||
- name: run full license export
|
||||
depends_on: ["clone"]
|
||||
image: registry.odit.services/hub/library/node:alpine
|
||||
image: registry.odit.services/hub/library/node:19.7.0-alpine3.16
|
||||
commands:
|
||||
- yarn
|
||||
- yarn licenses:export
|
||||
- npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8
|
||||
- pnpm i
|
||||
- pnpm licenses:export
|
||||
environment:
|
||||
NPM_REGISTRY_URL:
|
||||
from_secret: npm_url
|
||||
- name: push new licenses file to repo
|
||||
depends_on: ["run full license export"]
|
||||
image: appleboy/drone-git-push
|
||||
@@ -51,10 +62,8 @@ steps:
|
||||
password:
|
||||
from_secret: docker_password
|
||||
build_args:
|
||||
- NPM_REGISTRY_DOMAIN:
|
||||
from_secret: npmjs_domain
|
||||
- NPM_REGISTRY_TOKEN:
|
||||
from_secret: npmjs_token
|
||||
- NPM_REGISTRY_URL:
|
||||
from_secret: npm_url
|
||||
repo: lfk/frontend
|
||||
tags:
|
||||
- dev
|
||||
@@ -80,10 +89,8 @@ steps:
|
||||
password:
|
||||
from_secret: docker_password
|
||||
build_args:
|
||||
- NPM_REGISTRY_DOMAIN:
|
||||
from_secret: npmjs_domain
|
||||
- NPM_REGISTRY_TOKEN:
|
||||
from_secret: npmjs_token
|
||||
- NPM_REGISTRY_URL:
|
||||
from_secret: npm_url
|
||||
repo: lfk/frontend
|
||||
tags:
|
||||
- "${DRONE_TAG}"
|
||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,11 +1,6 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
*.map
|
||||
public/env.js
|
||||
public/index.html
|
||||
/dist
|
||||
.yarn
|
||||
.pnp.js
|
||||
.yarnrc.yml
|
||||
pnpm-lock.yaml
|
||||
.pnpm-store
|
339
CHANGELOG.md
339
CHANGELOG.md
@@ -2,9 +2,346 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||
|
||||
#### [1.3.2](https://git.odit.services/lfk/frontend/compare/1.3.1...1.3.2)
|
||||
|
||||
- fix(donors): Shortened texts [`b94e3b7`](https://git.odit.services/lfk/frontend/commit/b94e3b745f2febbe91e16a7a26f96b47d347ab92)
|
||||
- feat(donations): Resolve donations via donor [`6f337ae`](https://git.odit.services/lfk/frontend/commit/6f337aeee16267d1e67e3d3855b63b6f2e57979f)
|
||||
- fix(donors): Removed debug infos [`5d48060`](https://git.odit.services/lfk/frontend/commit/5d48060834717b2244172a0914e2690f8fe634d9)
|
||||
|
||||
#### [1.3.1](https://git.odit.services/lfk/frontend/compare/1.3.0...1.3.1)
|
||||
|
||||
> 19 April 2023
|
||||
|
||||
- feat(donations): Donation table filtering [`91ab199`](https://git.odit.services/lfk/frontend/commit/91ab199769c9f4f8051c74ad43a701db321f3995)
|
||||
- feat(donors): Added name and address filtering [`27b4dde`](https://git.odit.services/lfk/frontend/commit/27b4dde7551995c9d7e8ca33a9bd97d429a35801)
|
||||
- 🚀RELEASE v1.3.1 [`c842c20`](https://git.odit.services/lfk/frontend/commit/c842c203e2fbf0a201297d475db9047c0691bd52)
|
||||
- More filtering [`5bcfc8d`](https://git.odit.services/lfk/frontend/commit/5bcfc8db752fce96e9f523d14cefff1a4f675661)
|
||||
|
||||
#### [1.3.0](https://git.odit.services/lfk/frontend/compare/1.2.0...1.3.0)
|
||||
|
||||
> 19 April 2023
|
||||
|
||||
- feat(donations): Implemented donation deletion via confirm modal [`505fb8c`](https://git.odit.services/lfk/frontend/commit/505fb8cb08b81a7dcb08561bdda0f6464f140d3e)
|
||||
- 🚀RELEASE v1.3.0 [`e75be49`](https://git.odit.services/lfk/frontend/commit/e75be49be42c3d5581e2204bfa064bfa3778c1b6)
|
||||
- feat(donationsoverview): Switched donations overview to datatable [`133470b`](https://git.odit.services/lfk/frontend/commit/133470b6f2a63ec087f27c98ef260648a8672e5f)
|
||||
- feat(donations): Implemented add donation payment via datatable refresh [`e5c9265`](https://git.odit.services/lfk/frontend/commit/e5c92655886ad9a6fcd7565fadd7955c477c3595)
|
||||
- feat(donations): Donations reactive create and load into datatable [`02003ec`](https://git.odit.services/lfk/frontend/commit/02003ec80efc16aabd126710a6eeac18df43f841)
|
||||
- feat(DonationsOverview): i18n loading text [`8f8858f`](https://git.odit.services/lfk/frontend/commit/8f8858f10071ddf9988d0ec0e3c4a891db24a102)
|
||||
- new license file version [CI SKIP] [`4289034`](https://git.odit.services/lfk/frontend/commit/4289034436869750205a946247e7ab5f9892fe98)
|
||||
|
||||
#### [1.2.0](https://git.odit.services/lfk/frontend/compare/1.1.0...1.2.0)
|
||||
|
||||
> 19 April 2023
|
||||
|
||||
- feat(donoroverview): Added datatable formatters [`d98fb0d`](https://git.odit.services/lfk/frontend/commit/d98fb0d5b288c987a45ccbf2bb026ccaab539a71)
|
||||
- 🚀RELEASE v1.2.0 [`fdc7d80`](https://git.odit.services/lfk/frontend/commit/fdc7d80bbf9bd698128e9ec4f91fa813499777a9)
|
||||
- feat(donors): Load donors paginated [`5014bf5`](https://git.odit.services/lfk/frontend/commit/5014bf5bc5873cfe4ae04d71b4aff12b257dd2e3)
|
||||
- feat(donorsoverview): Dynamicly add newly generated donors [`352551e`](https://git.odit.services/lfk/frontend/commit/352551e168b5dced5e7353e82655908d82d28af0)
|
||||
- feat(donorsoverview): Implemented delete confirmation with datatable [`7aec050`](https://git.odit.services/lfk/frontend/commit/7aec050419f6f1bf853c3e1bc655b01725ed3b65)
|
||||
|
||||
#### [1.1.0](https://git.odit.services/lfk/frontend/compare/1.0.0...1.1.0)
|
||||
|
||||
> 19 April 2023
|
||||
|
||||
- 🚀RELEASE v1.1.0 [`0708cab`](https://git.odit.services/lfk/frontend/commit/0708cabc75e63a876e54a0b343318f8d934ae319)
|
||||
- feat(dashboar): Added total donors to overview [`e0b6148`](https://git.odit.services/lfk/frontend/commit/e0b61486b089aa1e611ef3569b1521fc331ec0e4)
|
||||
- feat(dashboard): Updated stats icons [`4fcb26c`](https://git.odit.services/lfk/frontend/commit/4fcb26cf9371e27e5d7e474b3558ef354e9114c0)
|
||||
- feat(dashboard): Added average sponsoring [`269def2`](https://git.odit.services/lfk/frontend/commit/269def20d114ededaba3153bbd50ec2ddd70e1c6)
|
||||
- feat(dashboard): Added total donations [`7b2b598`](https://git.odit.services/lfk/frontend/commit/7b2b59858839b98370af6fb1e6028ba0a1639186)
|
||||
- feat(dashboard): Added average distance [`b8de9e0`](https://git.odit.services/lfk/frontend/commit/b8de9e0e427b3a8b56e6354ad7168ae12c7cce85)
|
||||
- Lockfile [`3f86f74`](https://git.odit.services/lfk/frontend/commit/3f86f7412ffc4bc27328ad1f7d3c3118546e7e29)
|
||||
- Bumped client [`6454d96`](https://git.odit.services/lfk/frontend/commit/6454d960de3f9f5ea86679f157b3b7e7cffde74d)
|
||||
- new license file version [CI SKIP] [`2a64094`](https://git.odit.services/lfk/frontend/commit/2a640940062765a470387103a72ed14a2411d97b)
|
||||
|
||||
### [1.0.0](https://git.odit.services/lfk/frontend/compare/0.19.0...1.0.0)
|
||||
|
||||
> 19 April 2023
|
||||
|
||||
- 🚀RELEASE v1.0.0 [`8da7578`](https://git.odit.services/lfk/frontend/commit/8da7578a0a46a3e97d8c870e29399f6e8821c9fa)
|
||||
- Merge pull request 'feature/175-request_pagination' (#176) from feature/175-request_pagination into dev [`e9ce964`](https://git.odit.services/lfk/frontend/commit/e9ce9644ff03f981cec6e9ad56aa5fdf0ff71ef4)
|
||||
- Donation paginated loading [`ccf8656`](https://git.odit.services/lfk/frontend/commit/ccf865687b34016931a702c0a9b98a0a18e2b03a)
|
||||
- Paginated scan loading [`cac34db`](https://git.odit.services/lfk/frontend/commit/cac34db1fd3bf5dc7c7be64b3a76ca4c8c77938d)
|
||||
- Implemented Async loading of cards via pagination (500 cards per request) [`c33dfcf`](https://git.odit.services/lfk/frontend/commit/c33dfcfddddfed0902f3fa9b1d8a1d3e1560262f)
|
||||
- Paginated runner loading (1000 per page) [`faf3893`](https://git.odit.services/lfk/frontend/commit/faf3893180bb735bea6f1ea58c896686b89949fe)
|
||||
- Allways set loaded to true [`52439aa`](https://git.odit.services/lfk/frontend/commit/52439aa5bc8cfb1d78d5dfce55b1a0df640ad8f5)
|
||||
- Bumped lfk client [`019e14a`](https://git.odit.services/lfk/frontend/commit/019e14ab1f99906f13d36c7148d0f4b7894072f2)
|
||||
- new license file version [CI SKIP] [`a6ce04c`](https://git.odit.services/lfk/frontend/commit/a6ce04c90386f16abf235cc7b2f95aeea5011c7d)
|
||||
|
||||
#### [0.19.0](https://git.odit.services/lfk/frontend/compare/0.18.4...0.19.0)
|
||||
|
||||
> 17 April 2023
|
||||
|
||||
- 🚀RELEASE v0.19.0 [`94a64ca`](https://git.odit.services/lfk/frontend/commit/94a64ca69078c7fe2935eeb5f955fab95a79cb85)
|
||||
- Merge pull request 'feature/173-scanstation_configcodes' (#174) from feature/173-scanstation_configcodes into dev [`165c154`](https://git.odit.services/lfk/frontend/commit/165c1542338c58f2abf42fef2e7b84b40d1e2d9c)
|
||||
- I18n [`e60c09e`](https://git.odit.services/lfk/frontend/commit/e60c09e19c9cc20338906e84f4db4e009d926360)
|
||||
- Implemented config code generation [`4b63427`](https://git.odit.services/lfk/frontend/commit/4b6342727ee0ea38597750d8c99edc301f1ccc2d)
|
||||
- Styling [`4834d14`](https://git.odit.services/lfk/frontend/commit/4834d1484c3fb6ecd4a1b56aa9fbb8125c641a62)
|
||||
- Adjusted size on smaller devices [`318547d`](https://git.odit.services/lfk/frontend/commit/318547db46045e41de64d5688368e85cd6fb8035)
|
||||
- Lockfile [`947d01c`](https://git.odit.services/lfk/frontend/commit/947d01cf7fc7fe2ee88c56e017b0d663f1f3b4f9)
|
||||
- Barcode placeholder [`cb5fa52`](https://git.odit.services/lfk/frontend/commit/cb5fa52cd9a97490b50fb0c02c26615b49650c08)
|
||||
- Added bwip-js for barcode generation [`3563394`](https://git.odit.services/lfk/frontend/commit/3563394fb33d661890327e2ae08c400830b37844)
|
||||
|
||||
#### [0.18.4](https://git.odit.services/lfk/frontend/compare/0.18.3...0.18.4)
|
||||
|
||||
> 15 April 2023
|
||||
|
||||
- 🚀RELEASE v0.18.4 [`269d7a7`](https://git.odit.services/lfk/frontend/commit/269d7a7defdde059ef2bb5103262cf734e9babe9)
|
||||
- Hide address2 in orgs by default [`e95f233`](https://git.odit.services/lfk/frontend/commit/e95f2333b0b958ed00c0e097b43aac2e70ad0d38)
|
||||
|
||||
#### [0.18.3](https://git.odit.services/lfk/frontend/compare/0.18.2...0.18.3)
|
||||
|
||||
> 15 April 2023
|
||||
|
||||
- 🚀RELEASE v0.18.3 [`950217e`](https://git.odit.services/lfk/frontend/commit/950217e0a350f9999b879475edf41f2f11c48179)
|
||||
- Dont show adress 2 in runner overview [`5e65fb3`](https://git.odit.services/lfk/frontend/commit/5e65fb33013c3dad38e7ad6740b017ae206f278f)
|
||||
|
||||
#### [0.18.2](https://git.odit.services/lfk/frontend/compare/0.18.1...0.18.2)
|
||||
|
||||
> 15 April 2023
|
||||
|
||||
- 🚀RELEASE v0.18.2 [`2a294cd`](https://git.odit.services/lfk/frontend/commit/2a294cde040044bbebfb9c8b34b6c91b27772741)
|
||||
- Added timestamps to scanoverview [`cffbd17`](https://git.odit.services/lfk/frontend/commit/cffbd17dc77054048cc9b14891f960f9a3fd18cb)
|
||||
- Push in releaseit [`e95420d`](https://git.odit.services/lfk/frontend/commit/e95420d79c3227c0ca0cf0c0b599970c2b7d690e)
|
||||
|
||||
#### [0.18.1](https://git.odit.services/lfk/frontend/compare/0.18.0...0.18.1)
|
||||
|
||||
> 15 April 2023
|
||||
|
||||
- 🚀RELEASE v0.18.1 [`00de8c3`](https://git.odit.services/lfk/frontend/commit/00de8c3d75e90cd4614f42111f5f45bedde64130)
|
||||
- Missing scanstation translations [`30e3396`](https://git.odit.services/lfk/frontend/commit/30e33968978bf33cedb31bcbf63fac273e1664f5)
|
||||
- fix: button onclick a11y [`9fe53b0`](https://git.odit.services/lfk/frontend/commit/9fe53b0b9c71e8a6b4aa3f317327ffe729df0834)
|
||||
- fix(ConfirmStatsClientDeletion): ScanStationService -> StatsClientService [`5291e04`](https://git.odit.services/lfk/frontend/commit/5291e049a1d2e880fbe277095da91b70d4812c3f)
|
||||
- fix(ConfirmScanStationDeletion): donorControllerRemove -> scanStationControllerRemove [`08fbb50`](https://git.odit.services/lfk/frontend/commit/08fbb504c958415ce75e1e426296f870f0f1358d)
|
||||
|
||||
#### [0.18.0](https://git.odit.services/lfk/frontend/compare/0.17.3...0.18.0)
|
||||
|
||||
> 12 April 2023
|
||||
|
||||
- Moved filter function to typed version [`#171`](https://git.odit.services/lfk/frontend/issues/171)
|
||||
- ScansOverview: migrate to datatable [`#168`](https://git.odit.services/lfk/frontend/issues/168)
|
||||
- 🚀RELEASE v0.18.0 [`eb80406`](https://git.odit.services/lfk/frontend/commit/eb80406fdb8abf3f76bca742095e8f1f03480a56)
|
||||
- wip: ScansOverview -> new datatable [`c87561f`](https://git.odit.services/lfk/frontend/commit/c87561f63b90ab951daf91d9b8b54ba96a94cc7f)
|
||||
- Basic card table replace [`5662c3b`](https://git.odit.services/lfk/frontend/commit/5662c3b6da67c00c94254bf39f8820e531fc93ef)
|
||||
- RunnersOverview: table responsiveness [`bf1e715`](https://git.odit.services/lfk/frontend/commit/bf1e715261c0076fd8543dd1187c516209a73b16)
|
||||
- Basic card delete modal [`8ffe8ef`](https://git.odit.services/lfk/frontend/commit/8ffe8eff0623767809cdc49ea15cf2d30b609285)
|
||||
- wip: delete scans [`f105cc0`](https://git.odit.services/lfk/frontend/commit/f105cc0a41972261610d03015ecd5ee492dab8e2)
|
||||
- Added delete runner modal [`fd5db7d`](https://git.odit.services/lfk/frontend/commit/fd5db7d68ab4b32f7bb14bd0275a447b749e1fd8)
|
||||
- Extracted table bottom [`8f50555`](https://git.odit.services/lfk/frontend/commit/8f50555a06a35d0b11ac1d9201851d00d915c4b8)
|
||||
- fix: formatting [`9def0b2`](https://git.odit.services/lfk/frontend/commit/9def0b27c9958c5c75466f56c17b6dc4d44d2b50)
|
||||
- fix(CardsOverview): table scroll + checkbox posititioning [`9c13b2f`](https://git.odit.services/lfk/frontend/commit/9c13b2f9e938ad36cb74c66a8f91a5b872c8c2d1)
|
||||
- Extracted table header [`c241961`](https://git.odit.services/lfk/frontend/commit/c241961d0ab0d6b8c3bf0453c1a21d84027ed62f)
|
||||
- drop legacy dependencies [`c98eb49`](https://git.odit.services/lfk/frontend/commit/c98eb49ae31d1d28de79c7a9c945ade50828f07f)
|
||||
- scan delete working [`c7dcf7c`](https://git.odit.services/lfk/frontend/commit/c7dcf7c66d7141117e08462ad05f6f441565d012)
|
||||
- Add Card appends current cards [`195d182`](https://git.odit.services/lfk/frontend/commit/195d182cc977b4ac9f342f09a9ea69d461892a95)
|
||||
- Moved data loading to onmount [`a35af6f`](https://git.odit.services/lfk/frontend/commit/a35af6f02055115d60d040baaf22dabbeda38498)
|
||||
- Add runners reactivity [`06411dc`](https://git.odit.services/lfk/frontend/commit/06411dc14747fc9803da009a29688789c31dfc42)
|
||||
- quick cleanup [`ebdd1c2`](https://git.odit.services/lfk/frontend/commit/ebdd1c2c0c2191c60dfacd0c4be989748628ca63)
|
||||
- Extracted deletion into function of overview [`a0727a0`](https://git.odit.services/lfk/frontend/commit/a0727a02913708de06be8bf1b9f8cbae5489f080)
|
||||
- Moved update card logic to overview [`53b945c`](https://git.odit.services/lfk/frontend/commit/53b945c72fa5b30f03eff8bb83f8f25d85d79b07)
|
||||
- wip:ScanValid badge [`bd4952e`](https://git.odit.services/lfk/frontend/commit/bd4952ee575b4cd8caea3608b14d8c51b37a55eb)
|
||||
- yeeted extra table styling [`52a02c8`](https://git.odit.services/lfk/frontend/commit/52a02c82d2bdabdf3e632910aa415c1ffb97fd7f)
|
||||
- Added custom runner filter [`7c6d39b`](https://git.odit.services/lfk/frontend/commit/7c6d39b5fa60d1b834c9fb74dd26dfc2225161d6)
|
||||
- Scan reactive add [`f5d14f2`](https://git.odit.services/lfk/frontend/commit/f5d14f2e1810462cba99b2482320746f0750d4a1)
|
||||
- Moved code around [`239f79f`](https://git.odit.services/lfk/frontend/commit/239f79fecba732c807f4296cf524be15c1ccff1e)
|
||||
- Scan deletion [`7d8c68a`](https://git.odit.services/lfk/frontend/commit/7d8c68a4550407e8c871545e7a0c5f622a1e3b9c)
|
||||
- Added status filter function [`f6c1fea`](https://git.odit.services/lfk/frontend/commit/f6c1fea17ce64d890ac810d9dff058dd589c2989)
|
||||
- Added delete toast [`e2ddb5a`](https://git.odit.services/lfk/frontend/commit/e2ddb5a14cc8b450c00129709bf5b933a47866dd)
|
||||
- Fixed killing of the dom [`2f62c7a`](https://git.odit.services/lfk/frontend/commit/2f62c7ae89b7dda3bc0efd4d56e677898a3f197f)
|
||||
- Cards details modal [`f6985da`](https://git.odit.services/lfk/frontend/commit/f6985daec71e4d922acd8a9b2673fd41317b075f)
|
||||
- Reload table data on delete [`3e8dac3`](https://git.odit.services/lfk/frontend/commit/3e8dac3203f56723a3dad4a35887d60fc03d4ae3)
|
||||
- Added middle-name [`d811058`](https://git.odit.services/lfk/frontend/commit/d8110580e9a33cfb0c6e9cbdce630262d5b5d4c1)
|
||||
- Fixed id sorting [`7cec2a0`](https://git.odit.services/lfk/frontend/commit/7cec2a00c513678cae8643ef3905d566a538bde0)
|
||||
- Import add to datatable [`2a91562`](https://git.odit.services/lfk/frontend/commit/2a915620c9adcb481be53e3c919bd5f14a5108ee)
|
||||
- Scans deletion [`6d9d8a4`](https://git.odit.services/lfk/frontend/commit/6d9d8a4724135df98ed1bec74b0cc20a8bdbda53)
|
||||
- fix(DeleteRunnerModal): ESC key [`1df505e`](https://git.odit.services/lfk/frontend/commit/1df505ea00968f65e7aff85baa80f8935fd2eb7b)
|
||||
- Versionbuilder [`1613ae7`](https://git.odit.services/lfk/frontend/commit/1613ae7de6e84aa5f9df6774a8e820fcf515b14a)
|
||||
- Fixed edit dispatch [`c681570`](https://git.odit.services/lfk/frontend/commit/c68157013431eeb1150e37fea87da02d5f7b032e)
|
||||
- ScansOverview: use CardRunner link [`4b171fd`](https://git.odit.services/lfk/frontend/commit/4b171fd04f4fc895f9919294858e546686504c65)
|
||||
- Added custom filter to scan overview [`2c198cf`](https://git.odit.services/lfk/frontend/commit/2c198cfde89363142c1c958e6961b7823196e04e)
|
||||
- Disable sort for actions [`fc2c290`](https://git.odit.services/lfk/frontend/commit/fc2c2907c43e5b1fa192b571cf93b2f2e492158f)
|
||||
- Delete modal logic [`da33005`](https://git.odit.services/lfk/frontend/commit/da3300562a25cf39945a94f873ec27f0d5fb9dc0)
|
||||
- Module [`9ae5e62`](https://git.odit.services/lfk/frontend/commit/9ae5e62e5d84097082bc29fe20b329f6e614f6ed)
|
||||
- new license file version [CI SKIP] [`ed1caa7`](https://git.odit.services/lfk/frontend/commit/ed1caa7be7673ef08f9c0074101f3cfb52963ab7)
|
||||
- Merge pull request 'experiment/tanstack' (#172) from experiment/tanstack into dev [`d88f3a5`](https://git.odit.services/lfk/frontend/commit/d88f3a5a2737921536c5eb109734abafd7609e51)
|
||||
- Removed pnpm store [`eb13f03`](https://git.odit.services/lfk/frontend/commit/eb13f038a1990ed2966552ab1d36df4a76e5a99a)
|
||||
- dependency update, drop focusTrap, first tanstack experiment in RunnersOverview [`9bec95e`](https://git.odit.services/lfk/frontend/commit/9bec95ede80908e45ad0f18a66dbb45784f7e33e)
|
||||
- Added translations [`dcabed4`](https://git.odit.services/lfk/frontend/commit/dcabed4e93134a1f04305c47536f1cc93b0cfb31)
|
||||
- new license file version [CI SKIP] [`d0fe6a2`](https://git.odit.services/lfk/frontend/commit/d0fe6a2e8513638c4d8e2b3dafd48b91490eab3b)
|
||||
- Bumped pnpm to 8 [`ee91748`](https://git.odit.services/lfk/frontend/commit/ee91748b3c7c2fb4f196d76121713ac42465b9dd)
|
||||
- drop old datatables [`cb5f2b7`](https://git.odit.services/lfk/frontend/commit/cb5f2b73d0a5f71875f48d381382919f9bee364e)
|
||||
- wip: pagination [`a59dbbe`](https://git.odit.services/lfk/frontend/commit/a59dbbe50ede47e0dada906d10887cc6b1ae3264)
|
||||
- Added sort [`842248e`](https://git.odit.services/lfk/frontend/commit/842248e4c43bb94a0e73981d2d848e0723687932)
|
||||
- cleanup headers [`5ecf838`](https://git.odit.services/lfk/frontend/commit/5ecf838dd231269c3c4f0d1e3376ff157457b04a)
|
||||
- Moved filter function to shared [`2304b12`](https://git.odit.services/lfk/frontend/commit/2304b12c1cca6a64573223906ab0561ba9050ec5)
|
||||
- Group filters [`0283df2`](https://git.odit.services/lfk/frontend/commit/0283df22c84740dd978e09120985215b65d7a12a)
|
||||
- feat(RunnersOverview): row selection [`6993511`](https://git.odit.services/lfk/frontend/commit/6993511c67cc81303a1eece7fc2a4218dc818afc)
|
||||
- Basic details and delete buttons [`9363773`](https://git.odit.services/lfk/frontend/commit/9363773fa1910975d48746572fe166363986c6ab)
|
||||
- Added delete cards button [`70307a9`](https://git.odit.services/lfk/frontend/commit/70307a9e8272f22801c6765c947f8576cc1d1102)
|
||||
- ScansOverview: add ThFilterTrack [`008835c`](https://git.odit.services/lfk/frontend/commit/008835c24f833aebbecc53921b0901a76f25bf06)
|
||||
- Yeeted old datatable references [`8925261`](https://git.odit.services/lfk/frontend/commit/89252619b1f6d478c287178bfffc573cf574a8fa)
|
||||
- Added filter [`a9cdac4`](https://git.odit.services/lfk/frontend/commit/a9cdac4f74a80afe3d0ce677ed5256201b75b29b)
|
||||
- Moved sort onclick to header only [`3de7b63`](https://git.odit.services/lfk/frontend/commit/3de7b632e058e9ca9ab951de9e86cc2a40eceb68)
|
||||
- Switched drone to pnpm cache [`d79608e`](https://git.odit.services/lfk/frontend/commit/d79608edbb7ca68b0a26f6c021a141373308081f)
|
||||
- pagination size [`45a7a90`](https://git.odit.services/lfk/frontend/commit/45a7a90cb8ee933d064c5891b3532cb1ece38d38)
|
||||
- RunnersOverview: add filter keyboard support [`9415584`](https://git.odit.services/lfk/frontend/commit/94155845f020679e8765d198b5ab4735e1d1142c)
|
||||
- RunnersOverview: pass selectedRunners to actions [`d4ab76e`](https://git.odit.services/lfk/frontend/commit/d4ab76ea1be1789aaa53ca57d33b2356c6804ffd)
|
||||
- fix pagination next prev [`cac851f`](https://git.odit.services/lfk/frontend/commit/cac851f2b19dafa01b08d1da612e34cbe3eb4133)
|
||||
- Moved docker to pnpm with cache [`4cbd265`](https://git.odit.services/lfk/frontend/commit/4cbd26580e51b854fdfd369056f24a30b04100a1)
|
||||
- fix: TracksOverview [`c799088`](https://git.odit.services/lfk/frontend/commit/c7990882cfa4ac6c3a2ffa696dcbf3b62790c1fe)
|
||||
- Table header i18n [`070a20a`](https://git.odit.services/lfk/frontend/commit/070a20a2e599e9f00b1bd0e944f9d68d08dc1cba)
|
||||
- default to 50 rows pagination [`dc866dd`](https://git.odit.services/lfk/frontend/commit/dc866dd5403ae681193c5e325b1f6d4068f80b61)
|
||||
- improved tablefilters/groupFilter [`3abf608`](https://git.odit.services/lfk/frontend/commit/3abf608b15fcfc2cf65b705d28d394917e7ac333)
|
||||
- Updated group name conversion [`9111ad1`](https://git.odit.services/lfk/frontend/commit/9111ad147c7537036de834f80db6e557d8d6f0d9)
|
||||
- show certificate, runnercard, sponsoring contract section [`49c2cd5`](https://git.odit.services/lfk/frontend/commit/49c2cd5c4b32e1b75fe0dae48c1b470608936704)
|
||||
- add basic table styling [`c5e8409`](https://git.odit.services/lfk/frontend/commit/c5e84090795636c7a80d6cabe46168e436d40f50)
|
||||
- Cards deleted migrations [`ef077b4`](https://git.odit.services/lfk/frontend/commit/ef077b4e6ad722e6d1c9efd5060165f649fd94c9)
|
||||
- ignore pnpm store from now on [`fe62ad5`](https://git.odit.services/lfk/frontend/commit/fe62ad5539bc94b242c30c2952bf4c115cc9abfd)
|
||||
- ammendme [`d31fe23`](https://git.odit.services/lfk/frontend/commit/d31fe2363b0f167b937f7d67908e75c709b324d8)
|
||||
- updated default table row count [`245db06`](https://git.odit.services/lfk/frontend/commit/245db0617306bab23993caf00dc5ba0405e95625)
|
||||
- fix: checkbox styling [`64db553`](https://git.odit.services/lfk/frontend/commit/64db5531858b2275d27c53a347da6c0416a278e8)
|
||||
- Added distance conversion [`333214a`](https://git.odit.services/lfk/frontend/commit/333214aa8f945975ee2dcbd1cfa285e5e75c9d02)
|
||||
- Added filterfunctions [`a9a965d`](https://git.odit.services/lfk/frontend/commit/a9a965d6981b07b02e3f752e65e1e5c4e9df7147)
|
||||
- card/ThFilterRunner: text align left [`7083b3d`](https://git.odit.services/lfk/frontend/commit/7083b3d8d2c81fa4ab14f21d379befbb2dab5b6c)
|
||||
- import [`0240e1d`](https://git.odit.services/lfk/frontend/commit/0240e1dca2e162b8c79f294f5c651b4a38455235)
|
||||
- fix: table sort button + search style [`57dce34`](https://git.odit.services/lfk/frontend/commit/57dce34fc5b04c297c5341db24ec47c2f1d39595)
|
||||
- No longer switching pnpm store path [`e5241d6`](https://git.odit.services/lfk/frontend/commit/e5241d619beda90d271485b6c09142b1e7b9b5f1)
|
||||
- Reactivated generate cards modal [`8f33640`](https://git.odit.services/lfk/frontend/commit/8f33640bec8f79987b1319fb0765535fe493d496)
|
||||
- Always load bulk created cards [`f89023e`](https://git.odit.services/lfk/frontend/commit/f89023e24ab30c90a8cacf2e9590d1dfa1a743d5)
|
||||
- ScansOverview: fallback for manual scans [`1ec9556`](https://git.odit.services/lfk/frontend/commit/1ec9556aa64b996d0bdd9cd3ee50a767a154f5f6)
|
||||
- fix min-w th [`b35375c`](https://git.odit.services/lfk/frontend/commit/b35375c929ad8d525b2761c321ecef0399917828)
|
||||
- Close modal on delete and import toastify [`03b7ada`](https://git.odit.services/lfk/frontend/commit/03b7ada5ef3cbf64f178d3d0d88c448d76a42885)
|
||||
- Fixed styling [`13254b2`](https://git.odit.services/lfk/frontend/commit/13254b24dd75ca3eb47fd53d5368f047545e8420)
|
||||
- Implemented table buttons [`11a56f8`](https://git.odit.services/lfk/frontend/commit/11a56f87e89866bd9b2f7029ef683fd139d60aa4)
|
||||
- RunnersOverview: disable debug log [`2c992a0`](https://git.odit.services/lfk/frontend/commit/2c992a0e63fa72055d28785fe7f5c21fd26f1fea)
|
||||
- RunnersOverview: TopActionSection: add margin top [`88f96ac`](https://git.odit.services/lfk/frontend/commit/88f96acc3c31c748d3da29b4564dd681a89208b9)
|
||||
- Unused filter function value [`38d3e19`](https://git.odit.services/lfk/frontend/commit/38d3e1912cd6d5362db236c34e0c602b52060b73)
|
||||
- Make the linter happy [`fbc14fd`](https://git.odit.services/lfk/frontend/commit/fbc14fd7b47009bf64a8824e7b60373eea031e3f)
|
||||
- Dsable column filter for distance [`67eff0e`](https://git.odit.services/lfk/frontend/commit/67eff0eda9b7d49d1abe784247afdbfbd7a76b00)
|
||||
- getPaginationRowModel [`238082b`](https://git.odit.services/lfk/frontend/commit/238082b657998f513111e832556926decbe84e21)
|
||||
- -> onkeyup [`aecbabe`](https://git.odit.services/lfk/frontend/commit/aecbabe52221c0ab8d94a42dc8ac42f4f397fde8)
|
||||
- removed unused import [`9811ede`](https://git.odit.services/lfk/frontend/commit/9811ede3b23a488a49962a43dae5abad60f93341)
|
||||
- drop redundant button role [`e7eddb4`](https://git.odit.services/lfk/frontend/commit/e7eddb4f08632cc803b8752e3561319fab538843)
|
||||
- Filter id as equals [`2699b06`](https://git.odit.services/lfk/frontend/commit/2699b06d7c42608b1b3ce2037908a78143038441)
|
||||
- debug table [`fd0d45f`](https://git.odit.services/lfk/frontend/commit/fd0d45f721396787ad35fca2a7fd0f26d98b23e9)
|
||||
- add TODO for ScansOverview status filter [`9505c2b`](https://git.odit.services/lfk/frontend/commit/9505c2b03005f4e667082cb4527fea863dbbf7fc)
|
||||
|
||||
#### [0.17.3](https://git.odit.services/lfk/frontend/compare/0.17.2...0.17.3)
|
||||
|
||||
> 15 March 2023
|
||||
|
||||
- dependency fixes [`3ea7a01`](https://git.odit.services/lfk/frontend/commit/3ea7a015a9beba3c2e4d3eb966f24ff6d4ac786e)
|
||||
- 🚀RELEASE v0.17.3 [`85705b6`](https://git.odit.services/lfk/frontend/commit/85705b6e684d0c41d3dc770cef7bffb199101576)
|
||||
- set pnpm to @7 [`4432941`](https://git.odit.services/lfk/frontend/commit/44329413ed2ca23f74e86db041b2c25b2b1c2a2b)
|
||||
|
||||
#### [0.17.2](https://git.odit.services/lfk/frontend/compare/0.17.1...0.17.2)
|
||||
|
||||
> 15 March 2023
|
||||
|
||||
- new license file version [CI SKIP] [`00359d2`](https://git.odit.services/lfk/frontend/commit/00359d25c1bd3efdd6365bf284b3c07634049399)
|
||||
- 🚀RELEASE v0.17.2 [`46db68a`](https://git.odit.services/lfk/frontend/commit/46db68ab229dc740dfff8835ef916f2c2e629b27)
|
||||
- improved ThFilterGroup style [`f917018`](https://git.odit.services/lfk/frontend/commit/f917018fd92a8a5b034f735ac8b6e41995044317)
|
||||
|
||||
#### [0.17.1](https://git.odit.services/lfk/frontend/compare/0.17.0...0.17.1)
|
||||
|
||||
> 15 March 2023
|
||||
|
||||
- Revert "package dependency fixes, bumps, lockfile update" [`d8a3063`](https://git.odit.services/lfk/frontend/commit/d8a30637351e164599e07a6473d9a1d2b08d245d)
|
||||
- 🚀RELEASE v0.17.1 [`7b420c4`](https://git.odit.services/lfk/frontend/commit/7b420c430d27bf0fc85a4297780164a593fc1be3)
|
||||
|
||||
#### [0.17.0](https://git.odit.services/lfk/frontend/compare/0.16.5...0.17.0)
|
||||
|
||||
> 15 March 2023
|
||||
|
||||
- new license file version [CI SKIP] [`61328d2`](https://git.odit.services/lfk/frontend/commit/61328d20ed2cfd1df7d3c32767f9c64154879d6d)
|
||||
- wip: pnpm + node version [`732b2f0`](https://git.odit.services/lfk/frontend/commit/732b2f061e465bd08cf34c58d8cd6b021ba25dce)
|
||||
- package dependency fixes, bumps, lockfile update [`2c73b98`](https://git.odit.services/lfk/frontend/commit/2c73b9862d401dac15021eed3f7847d46132a8ed)
|
||||
- Fixed runners not showing up [`75bc89c`](https://git.odit.services/lfk/frontend/commit/75bc89ca3020c48f490c7602374616bd9461e78f)
|
||||
- add ThFilterRunner [`3a3e2f7`](https://git.odit.services/lfk/frontend/commit/3a3e2f71575d3a0e39a5e13b05cff932b8683ac9)
|
||||
- fix styling for table filters th border [`bea57aa`](https://git.odit.services/lfk/frontend/commit/bea57aa03acaaaa4b1860b30228dd5d1298317f8)
|
||||
- You can now create cards from runners by searching via #runnerid [`e8a0ad6`](https://git.odit.services/lfk/frontend/commit/e8a0ad6647ab39252865f62b755f27c34ac2d243)
|
||||
- remodelled for early return [`b869b5f`](https://git.odit.services/lfk/frontend/commit/b869b5fd2a01955fb21f936fa38eb5a9648e7de3)
|
||||
- 🚀RELEASE v0.17.0 [`6491af1`](https://git.odit.services/lfk/frontend/commit/6491af19e375cbeba7ddd94e463b4dfe308a70a8)
|
||||
- Wow this api is fun [`32a9074`](https://git.odit.services/lfk/frontend/commit/32a9074963cce3328f14b1f981ddd5ee49df0008)
|
||||
- Fixed double space in label [`92b89cc`](https://git.odit.services/lfk/frontend/commit/92b89cc4d88c9d5625c2ddf7c81c98494f7f5271)
|
||||
- UsersOverview: drop pfp [`30991d5`](https://git.odit.services/lfk/frontend/commit/30991d5364a09d517b2115a7e9ea3fbf1fe2e57d)
|
||||
- Switched license generation to cache registry and pnpm [`0a6d92a`](https://git.odit.services/lfk/frontend/commit/0a6d92a1f3c5562f11562c433b3a04e3eaae3da4)
|
||||
- Pinned pnpm in dockerfile, thx @philipp [`3a576d1`](https://git.odit.services/lfk/frontend/commit/3a576d1073ee503b68100e01054a1756bab62805)
|
||||
- Pinned ci node version [`b30b98b`](https://git.odit.services/lfk/frontend/commit/b30b98b521eda2bc7fc055097546f716e90d92ef)
|
||||
- Fixed pnpm being called without being installed [`43d82a2`](https://git.odit.services/lfk/frontend/commit/43d82a2af04af49c2169f78a0d0f27ef7e4d7558)
|
||||
- Merge pull request 'bugfix/162-create_card_modal' (#163) from bugfix/162-create_card_modal into dev [`6a4495b`](https://git.odit.services/lfk/frontend/commit/6a4495b8131a31cd48a608c2275e80494d0a0fb4)
|
||||
- Removed unused log [`268b1b1`](https://git.odit.services/lfk/frontend/commit/268b1b1d9830de196d1d95345d7a2467bbf19eb6)
|
||||
- Merge pull request 'filter by runner full names + "#<ID>"' (#160) from feature/159-cardsoverview-filter-for-runner-full-names-and-id into dev [`0625937`](https://git.odit.services/lfk/frontend/commit/0625937068f0786078ffd29b9c8bb54949350b6c)
|
||||
- UsersOverview: change profilepic scaling [`5cc8b08`](https://git.odit.services/lfk/frontend/commit/5cc8b0811cf290f97a4399b23c5ea4d961a5a91c)
|
||||
|
||||
#### [0.16.5](https://git.odit.services/lfk/frontend/compare/0.16.4...0.16.5)
|
||||
|
||||
> 14 March 2023
|
||||
|
||||
- 🚀RELEASE v0.16.5 [`3680533`](https://git.odit.services/lfk/frontend/commit/3680533eefef042fc77246dd3d374aafe10c428f)
|
||||
- new license file version [CI SKIP] [`405dfa0`](https://git.odit.services/lfk/frontend/commit/405dfa0c34ba87fc450c22e0e9974f92c4cdeffe)
|
||||
|
||||
#### [0.16.4](https://git.odit.services/lfk/frontend/compare/0.16.3...0.16.4)
|
||||
|
||||
> 14 March 2023
|
||||
|
||||
- fix: OrgDetail: clicking on address will toggle selfservice [`#158`](https://git.odit.services/lfk/frontend/issues/158)
|
||||
- 🚀RELEASE v0.16.4 [`5c2d154`](https://git.odit.services/lfk/frontend/commit/5c2d154ad180ce7916605871c63e2f5ac4428250)
|
||||
|
||||
#### [0.16.3](https://git.odit.services/lfk/frontend/compare/0.16.2...0.16.3)
|
||||
|
||||
> 23 February 2023
|
||||
|
||||
- 🚀RELEASE v0.16.3 [`f9cfd6b`](https://git.odit.services/lfk/frontend/commit/f9cfd6bd063b01a584774854d8fb5eab96f99528)
|
||||
- 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 -> 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)
|
||||
|
||||
@@ -1218,7 +1555,7 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
- init [`32357ec`](https://git.odit.services/lfk/frontend/commit/32357ece0a7195ea1135c9c3e4c6c84323f95b4d)
|
||||
- tmp [`1b7173c`](https://git.odit.services/lfk/frontend/commit/1b7173cda9134ee8058a00bdc030defa80d46bfc)
|
||||
- Login - move to env.js import [`8ef0b21`](https://git.odit.services/lfk/frontend/commit/8ef0b21819309752c573d0485f6514152fb684e6)
|
||||
- initial commit [`4bb3bae`](https://git.odit.services/lfk/frontend/commit/4bb3bae4e6fc89c35a8a2b36b7cd6e6d47958eae)
|
||||
- initial commit [`4bb3bae`](https://git.odit.services/lfk/frontend/commit/4bb3bae4e6fc89c35a8a2b36b7cd6e6d47958eae)
|
||||
- Initial license export [`4c96b9a`](https://git.odit.services/lfk/frontend/commit/4c96b9a3e04dbb7c021c71aa8828a29248509fbe)
|
||||
- 🚚 move to tinro svelte router [`a50ea15`](https://git.odit.services/lfk/frontend/commit/a50ea15b38023b867a9f7757e973184cbcdd2457)
|
||||
- new Dashboard [`7270ce9`](https://git.odit.services/lfk/frontend/commit/7270ce9d32869abd4f6ac65ab7c2c87363633cbe)
|
||||
|
13
Dockerfile
13
Dockerfile
@@ -1,11 +1,14 @@
|
||||
FROM registry.odit.services/hub/library/node:19.5.0-alpine3.16 as build
|
||||
FROM registry.odit.services/hub/library/node:19.7.0-alpine3.16 as build
|
||||
ARG NPM_REGISTRY_URL=https://registry.npmjs.org
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
RUN npx pnpm i
|
||||
COPY package.json *.config.js postcss.config.cjs tailwind.config.js vite.config.js index.html ./
|
||||
|
||||
COPY package.json pnpm-lock.yaml *.config.js *.config.cjs index.html ./
|
||||
RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8 && pnpm i
|
||||
|
||||
COPY src ./src
|
||||
COPY public ./public
|
||||
RUN yarn build
|
||||
RUN pnpm build
|
||||
|
||||
# final image
|
||||
FROM registry.odit.services/library/nginx-brotli:3.15 as final
|
||||
COPY --from=build /app/dist /usr/share/nginx/html
|
||||
|
@@ -13,7 +13,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.16.1-RELEASE_INFO</span>
|
||||
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-1.3.2-RELEASE_INFO</span>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<script src="/env.js"></script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
|
2
order.js
2
order.js
@@ -1,4 +1,4 @@
|
||||
const fs = require('fs');
|
||||
import fs from 'fs'
|
||||
// get all language files
|
||||
const files = fs.readdirSync('./src/locales/');
|
||||
files.forEach((f) => {
|
||||
|
117
package.json
117
package.json
@@ -1,56 +1,61 @@
|
||||
{
|
||||
"name": "@odit/lfk-frontend",
|
||||
"version": "0.16.1",
|
||||
"scripts": {
|
||||
"i18n-order": "node order.js",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"release": "release-it",
|
||||
"licenses:export": "license-exporter --json -o public"
|
||||
},
|
||||
"license": "CC-BY-NC-SA-4.0",
|
||||
"devDependencies": {
|
||||
"@odit/lfk-client-js": "0.13.1",
|
||||
"@odit/license-exporter": "0.0.11",
|
||||
"@sveltejs/vite-plugin-svelte": "1.0.0-next.6",
|
||||
"@types/html-minifier": "4.0.0",
|
||||
"auto-changelog": "2.2.1",
|
||||
"autoprefixer": "10.2.5",
|
||||
"check-password-strength": "2.0.2",
|
||||
"csvtojson": "2.0.10",
|
||||
"gridjs": "3.4.0",
|
||||
"html-minifier": "4.0.0",
|
||||
"localforage": "1.9.0",
|
||||
"marked": "2.0.3",
|
||||
"postcss": "8.2.10",
|
||||
"release-it": "14.6.1",
|
||||
"svelte": "3.37.0",
|
||||
"svelte-focus-trap": "1.2.0",
|
||||
"svelte-i18n": "3.3.9",
|
||||
"svelte-preprocess": "4.7.0",
|
||||
"svelte-select": "3.17.0",
|
||||
"tailwindcss": "3.2.4",
|
||||
"tinro": "0.6.1",
|
||||
"toastify-js": "1.10.0",
|
||||
"validator": "13.5.2",
|
||||
"vite": "2.1.5",
|
||||
"xlsx": "0.16.9"
|
||||
},
|
||||
"release-it": {
|
||||
"git": {
|
||||
"commit": true,
|
||||
"requireCleanWorkingDir": false,
|
||||
"commitMessage": "🚀RELEASE v${version}",
|
||||
"push": false,
|
||||
"tag": true,
|
||||
"tagName": null,
|
||||
"tagAnnotation": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "@odit/lfk-frontend",
|
||||
"version": "1.3.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"i18n-order": "node order.js",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"release": "release-it",
|
||||
"licenses:export": "license-exporter --json -o public"
|
||||
},
|
||||
"license": "CC-BY-NC-SA-4.0",
|
||||
"devDependencies": {
|
||||
"@odit/license-exporter": "0.0.12",
|
||||
"@sveltejs/vite-plugin-svelte": "2.0.4",
|
||||
"auto-changelog": "2.4.0",
|
||||
"autoprefixer": "10.4.14",
|
||||
"postcss": "8.4.21",
|
||||
"release-it": "15.10.1",
|
||||
"svelte-select": "3.17.0",
|
||||
"tailwindcss": "3.3.1",
|
||||
"vite": "4.2.1"
|
||||
},
|
||||
"release-it": {
|
||||
"git": {
|
||||
"commit": true,
|
||||
"requireCleanWorkingDir": false,
|
||||
"commitMessage": "🚀RELEASE v${version}",
|
||||
"push": true,
|
||||
"tag": true,
|
||||
"tagName": null,
|
||||
"tagAnnotation": "v${version}"
|
||||
},
|
||||
"npm": {
|
||||
"publish": false
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@odit/lfk-client-js": "1.1.0",
|
||||
"@paralleldrive/cuid2": "^2.2.0",
|
||||
"@tanstack/svelte-table": "^8.8.5",
|
||||
"bwip-js": "^3.4.0",
|
||||
"check-password-strength": "2.0.7",
|
||||
"csvtojson": "2.0.10",
|
||||
"gridjs": "3.4.0",
|
||||
"localforage": "1.10.0",
|
||||
"marked": "2.0.3",
|
||||
"svelte": "3.58.0",
|
||||
"svelte-i18n": "3.6.0",
|
||||
"tinro": "0.6.12",
|
||||
"toastify-js": "1.12.0",
|
||||
"validator": "13.9.0",
|
||||
"xlsx": "0.18.5"
|
||||
},
|
||||
"volta": {
|
||||
"node": "19.7.0"
|
||||
}
|
||||
}
|
||||
|
3950
pnpm-lock.yaml
generated
Normal file
3950
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,14 +1,16 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { RunnerCardService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let bulk_modal_open;
|
||||
export let current_cards;
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
$: card_count = 0;
|
||||
$: is_card_count_valid = card_count > 0;
|
||||
$: processed_last_submit = true;
|
||||
@@ -34,7 +36,7 @@
|
||||
text: $_("creating-blanco-cards"),
|
||||
duration: -1,
|
||||
}).showToast();
|
||||
RunnerCardService.runnerCardControllerPostBlancoBulk(card_count, false)
|
||||
RunnerCardService.runnerCardControllerPostBlancoBulk(card_count, true)
|
||||
.then((result) => {
|
||||
bulk_modal_open = false;
|
||||
//
|
||||
@@ -43,6 +45,7 @@
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
dispatch("created", {cards: result})
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -71,11 +74,11 @@
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_cards = current_cards.concat(result);
|
||||
const toast = Toastify({
|
||||
text: $_("generating-pdf"),
|
||||
duration: -1,
|
||||
}).showToast();
|
||||
dispatch("created", {cards: result})
|
||||
fetch(
|
||||
`${config.baseurl_documentserver}/cards?&download=true&key=${config.documentserver_key}`,
|
||||
{
|
||||
@@ -133,7 +136,7 @@
|
||||
{#if bulk_modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
bulk_modal_open = false;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import {
|
||||
RunnerCardService,
|
||||
RunnerService,
|
||||
@@ -9,24 +9,40 @@
|
||||
} from "@odit/lfk-client-js";
|
||||
import Select from "svelte-select";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let modal_open;
|
||||
export let current_cards;
|
||||
const getRunnerLabel = (option) =>
|
||||
option.firstname + " " + (option.middlename || "") + " " + option.lastname;
|
||||
const filterRunners = (label, filterText, option) =>
|
||||
label.toLowerCase().includes(filterText.toLowerCase()) ||
|
||||
option.value.toString().startsWith(filterText.toLowerCase());
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const getRunnerLabel = (option) => {
|
||||
if (option.middlename) {
|
||||
return option.firstname + " " + option.middlename + " " + option.lastname;
|
||||
}
|
||||
return option.firstname + " " + option.lastname;
|
||||
};
|
||||
|
||||
const filterRunners = (label, filterText, option) => {
|
||||
if (filterText.startsWith("#")) {
|
||||
return option.value.id == parseInt(filterText.replace("#", ""));
|
||||
}
|
||||
return (
|
||||
label.toLowerCase().includes(filterText.toLowerCase()) ||
|
||||
option.value.toString().startsWith(filterText.toLowerCase())
|
||||
);
|
||||
};
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
$: runner = 0;
|
||||
$: runners = [];
|
||||
$: enabled = true;
|
||||
$: processed_last_submit = true;
|
||||
|
||||
let loading = true;
|
||||
let runners = [];
|
||||
RunnerService.runnerControllerGetAll().then((val) => {
|
||||
runners = val.map((r) => {
|
||||
return { label: getRunnerLabel(r), value: r };
|
||||
});
|
||||
loading = false;
|
||||
});
|
||||
$: createbtnenabled = true;
|
||||
(() => {
|
||||
@@ -64,8 +80,7 @@
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_cards.push(result);
|
||||
current_cards = current_cards;
|
||||
dispatch("created", { cards: [result] });
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -82,65 +97,81 @@
|
||||
{#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">
|
||||
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" />
|
||||
data-id="modal_backdrop"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
aria-hidden="true">​</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">
|
||||
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">
|
||||
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" />
|
||||
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>
|
||||
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">
|
||||
{$_('create-a-new-card')}
|
||||
{$_("create-a-new-card")}
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
{$_('you-can-provide-a-runner-but-you-dont-have-to')}
|
||||
{$_('if-you-want-to-create-multiple-blanco-cards-try-the-add-bulk-button')}
|
||||
{$_("you-can-provide-a-runner-but-you-dont-have-to")}
|
||||
{$_(
|
||||
"if-you-want-to-create-multiple-blanco-cards-try-the-add-bulk-button"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-6 gap-6">
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="donor"
|
||||
class="block text-sm font-medium text-gray-700">{$_('runner')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("runner")}</label
|
||||
>
|
||||
<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"
|
||||
itemFilter={(label, filterText, option) => filterRunners(label, filterText, option)}
|
||||
itemFilter={(label, filterText, option) =>
|
||||
filterRunners(label, filterText, option)}
|
||||
items={runners}
|
||||
showChevron={true}
|
||||
placeholder={$_('search-for-runner-by-name-or-id')}
|
||||
noOptionsMessage={$_('no-runners-found')}
|
||||
on:select={(selectedValue) => (runner = selectedValue.detail.value.id)}
|
||||
on:clear={() => (runner = null)} />
|
||||
bind:loading
|
||||
showChevron={!loading}
|
||||
placeholder={$_("search-for-runner-by-name-or-id")}
|
||||
noOptionsMessage={$_("no-runners-found")}
|
||||
on:select={(selectedValue) =>
|
||||
(runner = selectedValue.detail.value.id)}
|
||||
on:clear={() => (runner = null)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -152,16 +183,18 @@
|
||||
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')}
|
||||
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')}
|
||||
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>
|
||||
|
@@ -1,26 +1,34 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { RunnerCardService, RunnerService } from "@odit/lfk-client-js";
|
||||
import Select from "svelte-select";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let edit_modal_open;
|
||||
export let current_cards;
|
||||
export let runner = {};
|
||||
export let editable = {};
|
||||
export let original_data = {};
|
||||
const getRunnerLabel = (option) =>
|
||||
option.firstname + " " + (option.middlename || "") + " " + option.lastname;
|
||||
const filterRunners = (label, filterText, option) =>
|
||||
label.toLowerCase().includes(filterText.toLowerCase()) ||
|
||||
option.value.toString().startsWith(filterText.toLowerCase());
|
||||
const filterRunners = (label, filterText, option) => {
|
||||
if (filterText.startsWith("#")) {
|
||||
return option.value.id == parseInt(filterText.replace("#",""))
|
||||
}
|
||||
return (
|
||||
label.toLowerCase().includes(filterText.toLowerCase()) ||
|
||||
option.value.toString().startsWith(filterText.toLowerCase())
|
||||
);
|
||||
};
|
||||
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
$: runners = [];
|
||||
$: enabled = true;
|
||||
$: processed_last_submit = true;
|
||||
const dispatch = createEventDispatcher();
|
||||
RunnerService.runnerControllerGetAll().then((val) => {
|
||||
runners = val.map((r) => {
|
||||
return { label: getRunnerLabel(r), value: r };
|
||||
@@ -63,8 +71,7 @@
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_cards[current_cards.findIndex((c) => c.id === id)] = result;
|
||||
current_cards = current_cards;
|
||||
dispatch('dataUpdated', {card: result});
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -81,7 +88,7 @@
|
||||
{#if edit_modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
edit_modal_open = false;
|
||||
|
16
src/components/cards/CardRunner.svelte
Normal file
16
src/components/cards/CardRunner.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let runner;
|
||||
</script>
|
||||
|
||||
{#if !runner}
|
||||
{$_("non-blanko")}
|
||||
{:else}
|
||||
<a href={`/runners/${runner.id}`}>
|
||||
{#if runner.middlename}
|
||||
{runner.firstname} {runner.middlename} {runner.lastname}
|
||||
{:else}
|
||||
{runner.firstname} {runner.lastname}
|
||||
{/if}
|
||||
</a>
|
||||
{/if}
|
16
src/components/cards/CardStatus.svelte
Normal file
16
src/components/cards/CardStatus.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let enabled = false;
|
||||
</script>
|
||||
|
||||
{#if enabled}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
|
||||
>{$_("enabled")}</span
|
||||
>
|
||||
{:else}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800"
|
||||
>{$_("disabled")}</span
|
||||
>
|
||||
{/if}
|
@@ -7,34 +7,48 @@
|
||||
$: current_cards = [];
|
||||
export let modal_open = false;
|
||||
export let bulk_modal_open = false;
|
||||
let addCards;
|
||||
</script>
|
||||
|
||||
<section class="container p-5">
|
||||
<span class="mb-1 text-3xl font-extrabold leading-tight">
|
||||
{$_('cards')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('CARD:CREATE')}
|
||||
{$_("cards")}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD: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">
|
||||
{$_('add-card')}
|
||||
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"
|
||||
>
|
||||
{$_("add-card")}
|
||||
</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
bulk_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-bulk-cards')}
|
||||
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-bulk-cards")}
|
||||
</button>
|
||||
{/if}
|
||||
</span>
|
||||
<CardsOverview bind:current_cards />
|
||||
<CardsOverview bind:current_cards bind:addCards />
|
||||
</section>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('CARD:CREATE')}
|
||||
<AddCardModal bind:current_cards bind:modal_open />
|
||||
<AddCardBulkModal bind:current_cards bind:bulk_modal_open />
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:CREATE")}
|
||||
<AddCardModal
|
||||
bind:modal_open
|
||||
on:created={(event) => {
|
||||
console.log(event)
|
||||
addCards(event.detail.cards);
|
||||
}}
|
||||
/>
|
||||
<AddCardBulkModal
|
||||
bind:bulk_modal_open
|
||||
on:created={(event) => {
|
||||
addCards(event.detail.cards);
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
@@ -6,276 +6,310 @@
|
||||
import CardsEmptyState from "./CardsEmptyState.svelte";
|
||||
import CardDetailModal from "./CardDetailModal.svelte";
|
||||
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
|
||||
import InputElement from "../shared/InputElement.svelte";
|
||||
import {
|
||||
createSvelteTable,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
renderComponent,
|
||||
} from "@tanstack/svelte-table";
|
||||
import { writable } from "svelte/store";
|
||||
import TableBottom from "../shared/TableBottom.svelte";
|
||||
import TableActions from "../shared/TableActions.svelte";
|
||||
import TableHeader from "../shared/TableHeader.svelte";
|
||||
import CardStatus from "./CardStatus.svelte";
|
||||
import CardRunner from "./CardRunner.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { runnerFilter, statusFilter } from "../shared/tablefilters";
|
||||
import DeleteCardModal from "./DeleteCardModal.svelte";
|
||||
|
||||
export let edit_modal_open = false;
|
||||
export let runner = {};
|
||||
export let editable = {};
|
||||
export let original_data = {};
|
||||
export let current_cards = [];
|
||||
$: filtered_cards = current_cards.filter(function (c) {
|
||||
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;
|
||||
}
|
||||
export const addCards = (cards) => {
|
||||
console.log(cards);
|
||||
current_cards = current_cards.concat(...cards);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_cards,
|
||||
}));
|
||||
};
|
||||
|
||||
$: dataLoaded = false;
|
||||
$: selected =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.index) || [];
|
||||
$: selectedCards =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.original) || [];
|
||||
$: active_delete = undefined;
|
||||
$: cards_show = generate_cards.length > 0;
|
||||
$: generate_cards = [];
|
||||
|
||||
const columns = [
|
||||
{
|
||||
accessorKey: "code",
|
||||
header: () => $_("code"),
|
||||
filterFn: `includesString`,
|
||||
},
|
||||
{
|
||||
accessorKey: "runner",
|
||||
header: () => $_("runner"),
|
||||
cell: (info) => {
|
||||
return renderComponent(CardRunner, { runner: info.getValue() });
|
||||
},
|
||||
filterFn: `runner`,
|
||||
},
|
||||
{
|
||||
accessorKey: "enabled",
|
||||
cell: (info) => {
|
||||
return renderComponent(CardStatus, { enabled: info.getValue() });
|
||||
},
|
||||
header: () => $_("status"),
|
||||
filterFn: `status`,
|
||||
},
|
||||
{
|
||||
accessorKey: "actions",
|
||||
header: () => $_("action"),
|
||||
cell: (info) => {
|
||||
return renderComponent(TableActions, {
|
||||
detailsAction: () => {
|
||||
open_edit_modal(
|
||||
current_cards[
|
||||
current_cards.findIndex((r) => r.id == info.row.original.id)
|
||||
]
|
||||
);
|
||||
},
|
||||
deleteAction: () => {
|
||||
active_delete =
|
||||
current_cards[
|
||||
current_cards.findIndex((r) => r.id == info.row.original.id)
|
||||
];
|
||||
},
|
||||
deleteEnabled:
|
||||
store.state.jwtinfo.userdetails.permissions.includes("CARD:DELETE"),
|
||||
});
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
},
|
||||
];
|
||||
|
||||
const options = writable({
|
||||
data: [],
|
||||
columns: columns,
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: 50,
|
||||
},
|
||||
},
|
||||
filterFns: {
|
||||
runner: runnerFilter,
|
||||
status: statusFilter,
|
||||
},
|
||||
enableRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
});
|
||||
$: searchvalue = "";
|
||||
$: searchvalue_lowercase = searchvalue.toLowerCase();
|
||||
$: active_deletes = [];
|
||||
$: cards_show = current_cards.some((r) => r.is_selected === true);
|
||||
$: generate_cards = current_cards.filter((r) => r.is_selected === true);
|
||||
const cards_promise = RunnerCardService.runnerCardControllerGetAll().then(
|
||||
(val) => {
|
||||
current_cards = 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) =>
|
||||
option?.firstname + " " + (option?.middlename || "") + " " + (option?.lastname || "{$_('non-blanko')}");
|
||||
|
||||
const table = createSvelteTable(options);
|
||||
|
||||
function open_edit_modal(card) {
|
||||
if(card.runner?.id){
|
||||
const getRunnerLabel = (option) =>
|
||||
option.firstname +
|
||||
" " +
|
||||
(option.middlename || "") +
|
||||
" " +
|
||||
option.lastname;
|
||||
if (card.runner?.id) {
|
||||
runner = Object.assign(
|
||||
{ runner },
|
||||
{ label: getRunnerLabel(card.runner), value: card.runner }
|
||||
);
|
||||
card.runner = card.runner.id;
|
||||
}
|
||||
else{
|
||||
card.runner=null;
|
||||
runner = null
|
||||
} else {
|
||||
card.runner = null;
|
||||
runner = null;
|
||||
}
|
||||
editable = Object.assign(editable, card);
|
||||
original_data = Object.assign(original_data, card);
|
||||
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;
|
||||
|
||||
async function deleteCard(delete_card_id) {
|
||||
await RunnerCardService.runnerCardControllerRemove(delete_card_id, true);
|
||||
current_cards = current_cards.filter((r) => r.id !== delete_card_id);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_cards,
|
||||
}));
|
||||
Toastify({
|
||||
text: $_("card-deleted"),
|
||||
duration: 3500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
let page = 0;
|
||||
while (page >= 0) {
|
||||
const cards = await RunnerCardService.runnerCardControllerGetAll(
|
||||
page,
|
||||
500
|
||||
);
|
||||
if (cards.length == 0) {
|
||||
page = -2;
|
||||
}
|
||||
|
||||
current_cards = current_cards.concat(...cards);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_cards,
|
||||
}));
|
||||
|
||||
dataLoaded = true;
|
||||
page++;
|
||||
}
|
||||
console.log("All cards loaded");
|
||||
});
|
||||
</script>
|
||||
|
||||
<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')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:UPDATE")}
|
||||
<CardDetailModal
|
||||
bind:current_cards
|
||||
bind:edit_modal_open
|
||||
bind:runner
|
||||
bind:editable
|
||||
bind:original_data />
|
||||
bind:original_data
|
||||
on:dataUpdated={(event) => {
|
||||
current_cards[
|
||||
current_cards.findIndex((c) => c.id === event.detail.card.id)
|
||||
] = event.detail.card;
|
||||
current_cards = current_cards;
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_cards,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('CARD:GET')}
|
||||
{#await cards_promise}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("CARD:GET")}
|
||||
<DeleteCardModal
|
||||
delete_card={active_delete}
|
||||
modal_open={active_delete != undefined}
|
||||
on:delete={(event) => {
|
||||
deleteCard(event.detail.id);
|
||||
}}
|
||||
/>
|
||||
{#if !dataLoaded}
|
||||
<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">{$_('loading-cards')}</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
role="alert"
|
||||
>
|
||||
<p class="font-bold">{$_("loading-cards")}</p>
|
||||
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_cards.length === 0}
|
||||
<CardsEmptyState />
|
||||
{:else}
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
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>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<span
|
||||
on:click={() => {
|
||||
const newstate = !current_cards.some((r) => r.is_selected === true);
|
||||
current_cards = current_cards.map((r) => {
|
||||
r.is_selected = newstate;
|
||||
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>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 virtual-wrapper"
|
||||
on:scroll={updateScroll}
|
||||
style="height: 70vh; width:100%"
|
||||
bind:this={ele}
|
||||
{:else if current_cards.length === 0}
|
||||
<CardsEmptyState />
|
||||
{:else}
|
||||
<div class="h-12 mt-1">
|
||||
{#if selected.length > 0}
|
||||
<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 inline-flex"
|
||||
id="options-menu"
|
||||
on:click={async () => {
|
||||
const prom = [];
|
||||
for (const card of selectedCards) {
|
||||
prom.push(
|
||||
await RunnerCardService.runnerCardControllerRemove(
|
||||
card.id,
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
await Promise.all(prom);
|
||||
for (const card of selectedCards) {
|
||||
current_cards = current_cards.filter((r) => r.id !== card.id);
|
||||
}
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_cards,
|
||||
}));
|
||||
$table.resetRowSelection();
|
||||
}}
|
||||
>
|
||||
{$_("delete-cards")}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
{#each filtered_cards as card, index}
|
||||
{#if card.code
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || card.runner?.firstname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || card.runner?.middlename
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || card.runner?.lastname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || should_display_based_on_id(card.id)}
|
||||
<tr data-rowid="card_{card.id}">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<input
|
||||
bind:checked={card.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">{card.code}</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
{#if card.runner}
|
||||
<a
|
||||
href="../runners/{card.runner.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{card.runner.firstname}
|
||||
{card.runner.middlename || ''}
|
||||
{card.runner.lastname}</a>
|
||||
{:else}{$_('non-blanko')}{/if}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
{#if card.enabled}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">{$_('enabled')}</span>
|
||||
{:else}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">{$_('disabled')}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{#if active_deletes[card.id] === true}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[card.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
RunnerCardService.runnerCardControllerRemove(card.id, false).then(
|
||||
(resp) => {
|
||||
current_cards = current_cards.filter(
|
||||
(obj) => obj.id !== card.id
|
||||
);
|
||||
Toastify({
|
||||
text: $_('card-deleted'),
|
||||
duration: 500,
|
||||
backgroundColor:
|
||||
'linear-gradient(to right, #00b09b, #96c93d)',
|
||||
}).showToast();
|
||||
}
|
||||
);
|
||||
}}
|
||||
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">
|
||||
<button
|
||||
on:click={() => {
|
||||
open_edit_modal(card);
|
||||
}}
|
||||
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</button>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('CARD:DELETE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[card.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>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/if}
|
||||
<GenerateRunnerCards
|
||||
cards_show={selected.length > 0}
|
||||
bind:generate_cards={selectedCards}
|
||||
/>
|
||||
</div>
|
||||
{/await}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
{#each $table.getHeaderGroups() as headerGroup}
|
||||
<tr class="select-none">
|
||||
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={$table.getIsAllRowsSelected()}
|
||||
indeterminate={$table.getIsSomeRowsSelected()}
|
||||
on:change={() => $table.toggleAllRowsSelected()}
|
||||
/>
|
||||
</th>
|
||||
{#each headerGroup.headers as header}
|
||||
<TableHeader {header} />
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each $table.getRowModel().rows as row}
|
||||
<tr>
|
||||
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={row.getIsSelected()}
|
||||
on:change={() => row.toggleSelected()}
|
||||
/>
|
||||
</td>
|
||||
{#each row.getVisibleCells() as cell}
|
||||
<td>
|
||||
<svelte:component
|
||||
this={flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<TableBottom {table} {selected} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
128
src/components/cards/DeleteCardModal.svelte
Normal file
128
src/components/cards/DeleteCardModal.svelte
Normal file
@@ -0,0 +1,128 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
export let modal_open;
|
||||
export let delete_card = {
|
||||
id: 0,
|
||||
code: "",
|
||||
runner: {
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
},
|
||||
};
|
||||
const dispatch = createEventDispatcher();
|
||||
onMount(() => {
|
||||
document.onkeydown = (e) => {
|
||||
e = e || window.event;
|
||||
if (e.key === "Escape") {
|
||||
modal_open = false;
|
||||
}
|
||||
if (e.keyCode === 13) {
|
||||
if (createbtnenabled === true) {
|
||||
createbtnenabled = false;
|
||||
submit();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
async function submit() {
|
||||
dispatch("delete", { id: delete_card.id });
|
||||
modal_open = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
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">​</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">
|
||||
{$_("confirm-delete")}
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
{$_("please-confirm-the-deletion-of-card")}
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
{$_("card")} #{delete_card.code}<br />
|
||||
<span class="inline-block">
|
||||
{$_("runner")}:
|
||||
{#if delete_card.runner}
|
||||
<span class="inline-block"
|
||||
>{delete_card.runner.firstname}
|
||||
{delete_card.runner.lastname}</span
|
||||
>
|
||||
{:else}
|
||||
{$_("non-blanko")}
|
||||
{/if}</span
|
||||
>
|
||||
</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={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"
|
||||
>
|
||||
{$_("delete")}
|
||||
</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}
|
57
src/components/cards/ThFilterRunner.svelte
Normal file
57
src/components/cards/ThFilterRunner.svelte
Normal file
@@ -0,0 +1,57 @@
|
||||
<script>
|
||||
export let handler;
|
||||
let filterValue = "";
|
||||
</script>
|
||||
|
||||
<th>
|
||||
<input
|
||||
on:input={() => {
|
||||
setTimeout(() => {
|
||||
const v = filterValue.toLowerCase();
|
||||
handler.filter(v, (c) => {
|
||||
// if (v === "") {
|
||||
// return c;
|
||||
// }
|
||||
|
||||
if (!c.runner && v === "blanko") {
|
||||
return "blanko";
|
||||
}
|
||||
|
||||
if (v.startsWith("#")) {
|
||||
return `#${c.runner?.id}`;
|
||||
}
|
||||
if (c.runner) {
|
||||
let runnerName = `${c.runner.firstname} ${c.runner.lastname}`;
|
||||
if (c.runner.middlename) {
|
||||
runnerName = `${c.runner.firstname} ${c.runner.middlename} ${c.runner.lastname}`;
|
||||
}
|
||||
runnerName = runnerName.toLowerCase();
|
||||
return runnerName;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
}, 150);
|
||||
}}
|
||||
bind:value={filterValue}
|
||||
type="text"
|
||||
name="runnerfilter"
|
||||
id="runnerfilter"
|
||||
/>
|
||||
</th>
|
||||
|
||||
<style>
|
||||
th {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
input {
|
||||
margin: -1px 0 0 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
border: none;
|
||||
text-align: left;
|
||||
background: inherit;
|
||||
outline: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
45
src/components/cards/ThFilterStatus.svelte
Normal file
45
src/components/cards/ThFilterStatus.svelte
Normal file
@@ -0,0 +1,45 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let handler;
|
||||
let selected = "all";
|
||||
</script>
|
||||
|
||||
<th>
|
||||
<select
|
||||
on:input={() => {
|
||||
setTimeout(() => {
|
||||
if (`${selected}`.trim()) {
|
||||
if (selected === "all") {
|
||||
handler.filter("", "enabled");
|
||||
} else {
|
||||
handler.filter(selected, "enabled");
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
}}
|
||||
bind:value={selected}
|
||||
name="statusfilter"
|
||||
id="statusfilter"
|
||||
>
|
||||
<option value="all">{$_("all")}</option>
|
||||
<option value="true">{$_("enabled")}</option>
|
||||
<option value="false">{$_("disabled")}</option>
|
||||
</select>
|
||||
</th>
|
||||
|
||||
<style>
|
||||
th {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
select {
|
||||
margin: -1px 0 0 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
border: none;
|
||||
text-align: left;
|
||||
background: inherit;
|
||||
outline: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import {
|
||||
GroupContactService,
|
||||
RunnerTeamService,
|
||||
@@ -145,7 +145,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -31,7 +31,7 @@
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
class="mb-4" />
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||
<table class="divide-y divide-gray-200 w-full">
|
||||
|
@@ -1,22 +1,224 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import StatCards from "./StatCards.svelte";
|
||||
import { StatsService } from "@odit/lfk-client-js";
|
||||
import StatCards from "./StatCard.svelte";
|
||||
import store from "../../store";
|
||||
import StatCard from "./StatCard.svelte";
|
||||
let navOpen = false;
|
||||
const stats_promise = StatsService.statsControllerGet();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="p-5 overflow-x-hidden"
|
||||
on:click={() => {
|
||||
navOpen = false;
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<h1 class="text-3xl leading-tight">
|
||||
<span class="font-extrabold">{$_('dashboard-title')}</span>
|
||||
<span class="font-extrabold">{$_("dashboard-title")}</span>
|
||||
<span>
|
||||
-
|
||||
{$_('dashboard-greeting')},
|
||||
<span
|
||||
class="text-blue-500">{store.state.jwtinfo.userdetails.firstname} {store.state.jwtinfo.userdetails.lastname}</span></span>
|
||||
{$_("dashboard-greeting")},
|
||||
<span class="text-blue-500"
|
||||
>{store.state.jwtinfo.userdetails.firstname}
|
||||
{store.state.jwtinfo.userdetails.lastname}</span
|
||||
></span
|
||||
>
|
||||
</h1>
|
||||
<StatCards />
|
||||
<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="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
|
||||
fill="currentColor"
|
||||
width="24"
|
||||
height="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M2 4h2v16H2V4zm4 0h1v16H6V4zm2 0h2v16H8V4zm3 0h2v16h-2V4zm3 0h2v16h-2V4zm3 0h1v16h-1V4zm2 0h3v16h-3V4z"
|
||||
/></svg
|
||||
>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title={$_("total-donors")}
|
||||
value={stats.total_donors}
|
||||
href="/donors/"
|
||||
>
|
||||
<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="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"
|
||||
/></svg
|
||||
>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title={$_("total-donation-count")}
|
||||
value={stats.total_donations}
|
||||
href="/donations/"
|
||||
>
|
||||
<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="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 4z"
|
||||
/></svg
|
||||
>
|
||||
</StatCard>
|
||||
<StatCard
|
||||
title={$_("average-donation")}
|
||||
value={`${(stats.average_donation / 100).toFixed(2)} €`}
|
||||
href="/donations/"
|
||||
>
|
||||
<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="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 4z"
|
||||
/></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={$_("average-distance")}
|
||||
value={`${(stats.average_distance / 1000).toFixed(2)} 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>
|
||||
|
22
src/components/dashboard/StatCard.svelte
Normal file
22
src/components/dashboard/StatCard.svelte
Normal file
@@ -0,0 +1,22 @@
|
||||
<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>
|
@@ -1,165 +0,0 @@
|
||||
<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/100).toFixed(2)} €</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}
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import {
|
||||
DonationService,
|
||||
DonorService,
|
||||
@@ -9,9 +9,10 @@
|
||||
} from "@odit/lfk-client-js";
|
||||
import Select from "svelte-select";
|
||||
import Toastify from "toastify-js";
|
||||
import { is_promise } from "svelte/internal";
|
||||
import { is_promise } from "svelte/internal";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let modal_open;
|
||||
export let current_donations;
|
||||
const dispatch = createEventDispatcher();
|
||||
const getDonorLabel = (option) =>
|
||||
option.firstname + " " + (option.middlename || "") + " " + option.lastname;
|
||||
const filterDonors = (label, filterText, option) =>
|
||||
@@ -59,16 +60,16 @@ import { is_promise } from "svelte/internal";
|
||||
let amount_cent = Math.floor(amount_input * 100);
|
||||
processed_last_submit = false;
|
||||
const toast = Toastify({
|
||||
text: $_('adding-donation'),
|
||||
text: $_("adding-donation"),
|
||||
duration: -1,
|
||||
}).showToast();
|
||||
if (is_fixed) {
|
||||
let postdata = {
|
||||
donor,
|
||||
amount: amount_cent,
|
||||
paidAmount: 0
|
||||
paidAmount: 0,
|
||||
};
|
||||
if(is_paid){
|
||||
if (is_paid) {
|
||||
postdata.paidAmount = amount_cent;
|
||||
}
|
||||
DonationService.donationControllerPostFixed(postdata)
|
||||
@@ -79,12 +80,11 @@ import { is_promise } from "svelte/internal";
|
||||
modal_open = false;
|
||||
//
|
||||
Toastify({
|
||||
text: $_('donation_added'),
|
||||
text: $_("donation_added"),
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_donations.push(result);
|
||||
current_donations = current_donations;
|
||||
dispatch("created", { donations: [result] });
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -108,12 +108,11 @@ import { is_promise } from "svelte/internal";
|
||||
modal_open = false;
|
||||
//
|
||||
Toastify({
|
||||
text: $_('donation_added'),
|
||||
text: $_("donation_added"),
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_donations.push(result);
|
||||
current_donations = current_donations;
|
||||
dispatch("created", { donations: [result] });
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -128,6 +127,207 @@ import { is_promise } from "svelte/internal";
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
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">​</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="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 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">
|
||||
{#if is_fixed}
|
||||
{$_("create-a-new-fixed-donation")}
|
||||
{:else}{$_("create-a-new-distance-donation")}{/if}
|
||||
</h3>
|
||||
<label class="content-center align-middle object-center">
|
||||
<span class="ml-2 text-base" class:text-gray-300={is_fixed}
|
||||
>{$_("distance-donation")}</span
|
||||
>
|
||||
<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"
|
||||
type="checkbox"
|
||||
bind:checked={is_fixed}
|
||||
/>
|
||||
<span class="ml-2 text-base" class:text-gray-300={!is_fixed}
|
||||
>{$_("fixed-donation")}</span
|
||||
>
|
||||
</label>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
{$_(
|
||||
"please-provide-the-nessecary-information-to-create-a-new-donation"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-6 gap-6">
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="donor"
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("donor")}</label
|
||||
>
|
||||
<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"
|
||||
itemFilter={(label, filterText, option) =>
|
||||
filterDonors(label, filterText, option)}
|
||||
items={donors}
|
||||
showChevron={true}
|
||||
placeholder={$_("search-for-donor-name-or-id")}
|
||||
noOptionsMessage={$_("no-donors-found")}
|
||||
on:select={(selectedValue) =>
|
||||
(donor = selectedValue.detail.value.id)}
|
||||
on:clear={() => (donors = null)}
|
||||
/>
|
||||
</div>
|
||||
{#if !is_fixed}
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="donor"
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("runner")}</label
|
||||
>
|
||||
<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"
|
||||
itemFilter={(label, filterText, option) =>
|
||||
filterDonors(label, filterText, option)}
|
||||
items={runners}
|
||||
showChevron={true}
|
||||
placeholder={$_("search-for-runner-by-name-or-id")}
|
||||
noOptionsMessage={$_("no-runners-found")}
|
||||
on:select={(selectedValue) =>
|
||||
(runner = selectedValue.detail.value.id)}
|
||||
on:clear={() => (runner = null)}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="donation_amount_eur"
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>
|
||||
{#if !is_fixed}
|
||||
{$_("amount-per-kilometer")}
|
||||
{:else}{$_("donation-amount")}{/if}</label
|
||||
>
|
||||
<div class="mt-1 flex rounded-md shadow-sm">
|
||||
<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={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 border-gray-300 border bg-gray-50 text-gray-500 p-2"
|
||||
placeholder="2.00"
|
||||
/>
|
||||
<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_amount_valid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("donation-amount-must-be-greater-that-0-00eur")}
|
||||
</span>
|
||||
{/if}
|
||||
</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 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}
|
||||
|
||||
<style>
|
||||
.toggle:before {
|
||||
content: "";
|
||||
@@ -152,173 +352,3 @@ import { is_promise } from "svelte/internal";
|
||||
left: 1.25rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
{#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">​</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="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 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">
|
||||
{#if is_fixed}
|
||||
{$_('create-a-new-fixed-donation')}
|
||||
{:else}{$_('create-a-new-distance-donation')}{/if}
|
||||
</h3>
|
||||
<label class="content-center align-middle object-center">
|
||||
<span
|
||||
class="ml-2 text-base"
|
||||
class:text-gray-300={is_fixed}>{$_('distance-donation')}</span>
|
||||
<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"
|
||||
type="checkbox"
|
||||
bind:checked={is_fixed} />
|
||||
<span
|
||||
class="ml-2 text-base "
|
||||
class:text-gray-300={!is_fixed}>{$_('fixed-donation')}</span>
|
||||
</label>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
{$_('please-provide-the-nessecary-information-to-create-a-new-donation')}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-6 gap-6">
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="donor"
|
||||
class="block text-sm font-medium text-gray-700">{$_('donor')}</label>
|
||||
<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"
|
||||
itemFilter={(label, filterText, option) => filterDonors(label, filterText, option)}
|
||||
items={donors}
|
||||
showChevron={true}
|
||||
placeholder={$_('search-for-donor-name-or-id')}
|
||||
noOptionsMessage={$_('no-donors-found')}
|
||||
on:select={(selectedValue) => (donor = selectedValue.detail.value.id)}
|
||||
on:clear={() => (donors = null)} />
|
||||
</div>
|
||||
{#if !is_fixed}
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="donor"
|
||||
class="block text-sm font-medium text-gray-700">{$_('runner')}</label>
|
||||
<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"
|
||||
itemFilter={(label, filterText, option) => filterDonors(label, filterText, option)}
|
||||
items={runners}
|
||||
showChevron={true}
|
||||
placeholder={$_('search-for-runner-by-name-or-id')}
|
||||
noOptionsMessage={$_('no-runners-found')}
|
||||
on:select={(selectedValue) => (runner = selectedValue.detail.value.id)}
|
||||
on:clear={() => (runner = null)} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="donation_amount_eur"
|
||||
class="block text-sm font-medium text-gray-700">
|
||||
{#if !is_fixed}
|
||||
{$_('amount-per-kilometer')}
|
||||
{:else}{$_('donation-amount')}{/if}</label>
|
||||
<div class="mt-1 flex rounded-md shadow-sm">
|
||||
<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={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 border-gray-300 border bg-gray-50 text-gray-500 p-2"
|
||||
placeholder="2.00" />
|
||||
<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_amount_valid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('donation-amount-must-be-greater-that-0-00eur')}
|
||||
</span>
|
||||
{/if}
|
||||
</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 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}
|
||||
|
@@ -1,19 +1,21 @@
|
||||
<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";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
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;
|
||||
const dispatch = createEventDispatcher();
|
||||
$: processed_last_submit = true;
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
$: createbtnenabled = is_paid_amount_valid && !(paid_amount_input*100 == original_data.paidAmount)
|
||||
$: createbtnenabled =
|
||||
is_paid_amount_valid &&
|
||||
!(paid_amount_input * 100 == original_data.paidAmount);
|
||||
$: is_paid_amount_valid = paid_amount_input > 0;
|
||||
(() => {
|
||||
document.onkeydown = (e) => {
|
||||
@@ -33,61 +35,56 @@
|
||||
if (processed_last_submit === true) {
|
||||
processed_last_submit = false;
|
||||
const toast = Toastify({
|
||||
text: $_('updating-donation'),
|
||||
text: $_("updating-donation"),
|
||||
duration: -1,
|
||||
}).showToast();
|
||||
const editable = Object.assign({}, original_data);
|
||||
editable.donor = editable.donor.id;
|
||||
editable.paidAmount = paid_amount_input*100;
|
||||
if(editable.responseType == "DISTANCEDONATION" || editable.runner){
|
||||
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();
|
||||
});
|
||||
DonationService.donationControllerPutDistance(
|
||||
original_data.id,
|
||||
editable
|
||||
)
|
||||
.then((result) => {
|
||||
payment_modal_open = false;
|
||||
//
|
||||
Toastify({
|
||||
text: $_("donation-updated"),
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
dispatch("created", { donation: response });
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
})
|
||||
.finally(() => {
|
||||
processed_last_submit = true;
|
||||
//
|
||||
toast.hideToast();
|
||||
});
|
||||
} else {
|
||||
DonationService.donationControllerPutFixed(original_data.id, editable)
|
||||
.then((result) => {
|
||||
payment_modal_open = false;
|
||||
//
|
||||
Toastify({
|
||||
text: $_("donation-updated"),
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
dispatch("created", { donation: response });
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
})
|
||||
.finally(() => {
|
||||
processed_last_submit = true;
|
||||
//
|
||||
toast.hideToast();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,57 +93,71 @@
|
||||
{#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">
|
||||
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" />
|
||||
data-id="modal_backdrop"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
aria-hidden="true">​</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">
|
||||
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">
|
||||
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" />
|
||||
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>
|
||||
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')}
|
||||
{$_("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')}
|
||||
{$_(
|
||||
"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
|
||||
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}
|
||||
@@ -156,47 +167,55 @@
|
||||
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" />
|
||||
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>
|
||||
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>
|
||||
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')}
|
||||
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 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}
|
||||
|
122
src/components/donations/DeleteDonationModal.svelte
Normal file
122
src/components/donations/DeleteDonationModal.svelte
Normal file
@@ -0,0 +1,122 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
export let modal_open;
|
||||
export let delete_donation = {
|
||||
id: 0,
|
||||
runner: {
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
},
|
||||
donor: {
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
},
|
||||
};
|
||||
const dispatch = createEventDispatcher();
|
||||
onMount(() => {
|
||||
document.onkeydown = (e) => {
|
||||
e = e || window.event;
|
||||
if (e.key === "Escape") {
|
||||
modal_open = false;
|
||||
}
|
||||
if (e.keyCode === 13) {
|
||||
if (createbtnenabled === true) {
|
||||
createbtnenabled = false;
|
||||
submit();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
async function submit() {
|
||||
dispatch("delete", { id: delete_donation.id });
|
||||
modal_open = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
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">​</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="M14 2a8 8 0 013.3 15.3A8 8 0 116.7 6.7 8 8 0 0114 2zm-3 7H9v1a2.5 2.5 0 00-.16 5h2.25a.5.5 0 010 1H7v2h2v1h2v-1a2.5 2.5 0 00.16-5H8.91a.5.5 0 010-1H13v-2h-2V9zm3-5a5.99 5.99 0 00-4.48 2.01 8 8 0 018.47 8.47A6 6 0 0014 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">
|
||||
{$_("confirm-delete")}
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
{$_("please-confirm-the-deletion-of-donation")}
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<span class="inline-block"
|
||||
><b>{$_("donor")}</b>: {delete_donation.donor.firstname}
|
||||
{delete_donation.donor.lastname}</span
|
||||
>
|
||||
</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={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"
|
||||
>
|
||||
{$_("delete")}
|
||||
</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}
|
18
src/components/donations/DonationDonor.svelte
Normal file
18
src/components/donations/DonationDonor.svelte
Normal file
@@ -0,0 +1,18 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let donor;
|
||||
</script>
|
||||
|
||||
{#if !donor || donor.firstname == 0}
|
||||
{$_("donor-has-no-associated-donations")}
|
||||
{:else}
|
||||
<div class="flex items-center">
|
||||
<a
|
||||
href="../donors/{donor.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
|
||||
>{donor.firstname}
|
||||
{#if donor.middlename}{donor.middlename}{/if}
|
||||
{donor.lastname}</a
|
||||
>
|
||||
</div>
|
||||
{/if}
|
18
src/components/donations/DonationRunner.svelte
Normal file
18
src/components/donations/DonationRunner.svelte
Normal file
@@ -0,0 +1,18 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let runner;
|
||||
</script>
|
||||
|
||||
{#if !runner || runner.firstname == 0}
|
||||
{$_("fixed-donation")}
|
||||
{:else}
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
<a
|
||||
href="../runners/{runner.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800"
|
||||
>{runner.firstname}
|
||||
{#if runner.middlename}{runner.middlename}{/if}
|
||||
{runner.lastname}</a
|
||||
>
|
||||
</div>
|
||||
{/if}
|
16
src/components/donations/DonationStatus.svelte
Normal file
16
src/components/donations/DonationStatus.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let status;
|
||||
</script>
|
||||
|
||||
{#if 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}
|
21
src/components/donations/DonationTableAction.svelte
Normal file
21
src/components/donations/DonationTableAction.svelte
Normal file
@@ -0,0 +1,21 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import TableActions from "../shared/TableActions.svelte";
|
||||
|
||||
export let detailsLink;
|
||||
export let detailsAction;
|
||||
export let deleteEnabled;
|
||||
export let deleteAction;
|
||||
export let paymentAction;
|
||||
</script>
|
||||
|
||||
<button
|
||||
on:click={paymentAction}
|
||||
class="text-[#025a21] hover:text-green-900 mr-4">{$_("enter-payment")}</button
|
||||
>
|
||||
<TableActions
|
||||
bind:detailsAction
|
||||
bind:detailsLink
|
||||
bind:deleteAction
|
||||
bind:deleteEnabled
|
||||
/>
|
@@ -5,25 +5,33 @@
|
||||
import DonationsOverview from "./DonationsOverview.svelte";
|
||||
$: current_donations = [];
|
||||
export let modal_open = false;
|
||||
let addDonations;
|
||||
</script>
|
||||
|
||||
<section class="container p-5">
|
||||
<span class="mb-1 text-3xl font-extrabold leading-tight">
|
||||
{$_('donations')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('DONATION:CREATE')}
|
||||
{$_("donations")}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("DONATION: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">
|
||||
{$_('add-donation')}
|
||||
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"
|
||||
>
|
||||
{$_("add-donation")}
|
||||
</button>
|
||||
{/if}
|
||||
</span>
|
||||
<DonationsOverview bind:current_donations />
|
||||
<DonationsOverview bind:current_donations bind:addDonations />
|
||||
</section>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('DONATION:CREATE')}
|
||||
<AddDonationModal bind:current_donations bind:modal_open />
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:CREATE")}
|
||||
<AddDonationModal
|
||||
on:created={(event) => {
|
||||
console.log(event)
|
||||
addDonations(event.detail.donations);
|
||||
}}
|
||||
bind:modal_open
|
||||
/>
|
||||
{/if}
|
||||
|
@@ -1,235 +1,284 @@
|
||||
<script>
|
||||
import { getLocaleFromNavigator, _ } from "svelte-i18n";
|
||||
import { DonationService, DonorService } from "@odit/lfk-client-js";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { DonationService } from "@odit/lfk-client-js";
|
||||
import store from "../../store";
|
||||
import Toastify from "toastify-js";
|
||||
import DonationsEmptyState from "./DonationsEmptyState.svelte";
|
||||
import AddDonationPaymentModal from "./AddDonationPaymentModal.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import {
|
||||
createSvelteTable,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
renderComponent,
|
||||
} from "@tanstack/svelte-table";
|
||||
import { writable } from "svelte/store";
|
||||
import TableBottom from "../shared/TableBottom.svelte";
|
||||
import InputElement from "../shared/InputElement.svelte";
|
||||
import TableHeader from "../shared/TableHeader.svelte";
|
||||
import DonationDonor from "./DonationDonor.svelte";
|
||||
import DonationRunner from "./DonationRunner.svelte";
|
||||
import DonationStatus from "./DonationStatus.svelte";
|
||||
import DonationTableAction from "./DonationTableAction.svelte";
|
||||
import DeleteDonationModal from "./DeleteDonationModal.svelte";
|
||||
import { donationDonorFilter, donationRunnerFilter } from "../shared/tablefilters";
|
||||
$: searchvalue = "";
|
||||
$: active_deletes = [];
|
||||
$: active_edits = [];
|
||||
$: selectedDonations =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.original) || [];
|
||||
$: selected =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.index) || [];
|
||||
$: dataLoaded = false;
|
||||
|
||||
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(
|
||||
(val) => {
|
||||
current_donations = val;
|
||||
}
|
||||
);
|
||||
function should_display_based_on_id(id) {
|
||||
if (searchvalue.toString().slice(-1) === "*") {
|
||||
return id.toString().startsWith(searchvalue.replace("*", ""));
|
||||
}
|
||||
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;
|
||||
export const addDonations = (donations) => {
|
||||
current_donations = current_donations.concat(...donations);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_donations,
|
||||
}));
|
||||
};
|
||||
|
||||
//Section table
|
||||
const columns = [
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: () => "id",
|
||||
filterFn: `equalsString`,
|
||||
},
|
||||
{
|
||||
accessorKey: "donor",
|
||||
header: () => $_("donor"),
|
||||
cell: (info) => {
|
||||
return renderComponent(DonationDonor, { donor: info.getValue() });
|
||||
},
|
||||
filterFn: `donor`,
|
||||
},
|
||||
{
|
||||
accessorKey: "runner",
|
||||
header: () => $_("runner"),
|
||||
cell: (info) => {
|
||||
return renderComponent(DonationRunner, { runner: info.getValue() });
|
||||
},
|
||||
filterFn: `runner`,
|
||||
},
|
||||
{
|
||||
accessorKey: "amountPerDistance",
|
||||
header: () => $_("amount-per-kilometer"),
|
||||
cell: (info) => {
|
||||
if (!info.getValue()) {
|
||||
return $_("fixed-donation");
|
||||
}
|
||||
return `${(info.getValue() / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString("de-DE", { valute: "EUR" })} €`;
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "amount",
|
||||
header: () => $_("donation-amount"),
|
||||
cell: (info) => {
|
||||
return `${(info.getValue() / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString("de-DE", { valute: "EUR" })} €`;
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "paidAmount",
|
||||
header: () => $_("total-paid-amount"),
|
||||
cell: (info) => {
|
||||
return `${(info.getValue() / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString("de-DE", { valute: "EUR" })} €`;
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: () => $_("status"),
|
||||
cell: (info) => {
|
||||
return renderComponent(DonationStatus, { status: info.getValue() });
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "actions",
|
||||
header: () => $_("action"),
|
||||
cell: (info) => {
|
||||
return renderComponent(DonationTableAction, {
|
||||
detailsLink: `./${info.row.original.id}`,
|
||||
deleteAction: () => {
|
||||
active_deletes = current_donations.filter(
|
||||
(r) => r.id == info.row.original.id
|
||||
);
|
||||
},
|
||||
paymentAction: () => {
|
||||
active_edits = current_donations.filter(
|
||||
(r) => r.id == info.row.original.id
|
||||
);
|
||||
},
|
||||
deleteEnabled:
|
||||
store.state.jwtinfo.userdetails.permissions.includes(
|
||||
"DONATION:DELETE"
|
||||
),
|
||||
});
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
},
|
||||
];
|
||||
const options = writable({
|
||||
data: [],
|
||||
columns: columns,
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: 50,
|
||||
},
|
||||
},
|
||||
filterFns: {
|
||||
donor: donationDonorFilter,
|
||||
runner: donationRunnerFilter,
|
||||
},
|
||||
enableRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
});
|
||||
const table = createSvelteTable(options);
|
||||
|
||||
async function deleteDonation(delete_donation_id) {
|
||||
await DonationService.donationControllerRemove(delete_donation_id, true);
|
||||
current_donations = current_donations.filter(
|
||||
(r) => r.id !== delete_donation_id
|
||||
);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_donations,
|
||||
}));
|
||||
Toastify({
|
||||
text: $_("donation-deleted"),
|
||||
duration: 3500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
let page = 0;
|
||||
while (page >= 0) {
|
||||
const donations = await DonationService.donationControllerGetAll(
|
||||
page,
|
||||
500
|
||||
);
|
||||
if (donations.length == 0) {
|
||||
page = -2;
|
||||
}
|
||||
|
||||
current_donations = current_donations.concat(...donations);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_donations,
|
||||
}));
|
||||
|
||||
dataLoaded = true;
|
||||
page++;
|
||||
}
|
||||
console.log("All donations loaded");
|
||||
});
|
||||
</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')}
|
||||
{#await donations_promise}
|
||||
<AddDonationPaymentModal
|
||||
original_data={active_edits[0]}
|
||||
payment_modal_open={active_edits.length > 0}
|
||||
paid_amount_input={(active_edits[0]?.paidAmount || 0) / 100}
|
||||
on:created={(event) => {
|
||||
current_donations[
|
||||
current_donations.findIndex((d) => d.id === event.detail.donation.id)
|
||||
].paidAmount = event.detail.donation.paidAmount;
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_donations,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
<DeleteDonationModal
|
||||
delete_donation={active_deletes[0]}
|
||||
modal_open={active_deletes.length > 0}
|
||||
on:delete={(event) => {
|
||||
deleteDonation(event.detail.id);
|
||||
}}
|
||||
/>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("DONATION:GET")}
|
||||
{#if !dataLoaded}
|
||||
<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">donations are being loaded</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
role="alert"
|
||||
>
|
||||
<p class="font-bold">{$_("donations-are-being-loaded")}</p>
|
||||
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_donations.length === 0}
|
||||
<DonationsEmptyState />
|
||||
{: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">
|
||||
{$_('donor')}
|
||||
</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">
|
||||
{$_('amount-per-kilometer')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('donation-amount')}
|
||||
</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">
|
||||
<span class="sr-only">{$_('action')}</span>
|
||||
{:else if current_donations.length === 0}
|
||||
<DonationsEmptyState />
|
||||
{:else}
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_("datatable.search")}
|
||||
aria-label={$_("datatable.search")}
|
||||
class="mb-4"
|
||||
/>
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
|
||||
>
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
{#each $table.getHeaderGroups() as headerGroup}
|
||||
<tr class="select-none">
|
||||
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={$table.getIsAllRowsSelected()}
|
||||
indeterminate={$table.getIsSomeRowsSelected()}
|
||||
on:change={() => $table.toggleAllRowsSelected()}
|
||||
/>
|
||||
</th>
|
||||
{#each headerGroup.headers as header}
|
||||
<TableHeader {header} />
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
{#each current_donations as donation}
|
||||
{#if donation.donor.firstname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || donation.donor.lastname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || donation.runner?.firstname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || donation.runner?.lastname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || should_display_based_on_id(donation.id)}
|
||||
<tr data-rowid="donation_{donation.id}">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<a
|
||||
href="../donors/{donation.donor.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{donation.donor.firstname}
|
||||
{donation.donor.middlename || ''}
|
||||
{donation.donor.lastname}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if donation.runner}
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
<a
|
||||
href="../runners/{donation.runner.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{donation.runner.firstname}
|
||||
{donation.runner.middlename || ''}
|
||||
{donation.runner.lastname}</a>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{$_('fixed-donation')}
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if donation.amountPerDistance}
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{(donation.amountPerDistance / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString('de-DE', { valute: 'EUR' })}€
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{$_('fixed-donation')}
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{(donation.amount / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString('de-DE', { valute: 'EUR' })}€
|
||||
</div>
|
||||
</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}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[donation.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
DonationService.donationControllerRemove(donation.id, false).then(
|
||||
(resp) => {
|
||||
current_donations = current_donations.filter(
|
||||
(obj) => obj.id !== donation.id
|
||||
);
|
||||
Toastify({
|
||||
text: $_('donation-deleted'),
|
||||
duration: 500,
|
||||
backgroundColor:
|
||||
'linear-gradient(to right, #00b09b, #96c93d)',
|
||||
}).showToast();
|
||||
}
|
||||
);
|
||||
}}
|
||||
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">
|
||||
<button
|
||||
on:click={() => {open_payment_modal(donation);}}
|
||||
class="text-[#025a21] hover:text-green-900 mr-4">{$_('enter-payment')}</button>
|
||||
<a
|
||||
href="./{donation.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('DONATION:DELETE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[donation.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>
|
||||
{/each}
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each $table.getRowModel().rows as row}
|
||||
<tr>
|
||||
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={row.getIsSelected()}
|
||||
on:change={() => row.toggleSelected()}
|
||||
/>
|
||||
</td>
|
||||
{#each row.getVisibleCells() as cell}
|
||||
<td>
|
||||
<svelte:component
|
||||
this={flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
<div class="h-2" />
|
||||
<TableBottom {table} {selected} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
@@ -1,15 +1,13 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
import {
|
||||
DonorService
|
||||
} from "@odit/lfk-client-js";
|
||||
|
||||
import { DonorService } from "@odit/lfk-client-js";
|
||||
import isEmail from "validator/es/lib/isEmail";
|
||||
import isMobilePhone from "validator/es/lib/isMobilePhone";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let modal_open;
|
||||
export let current_donors;
|
||||
let firstname_input;
|
||||
let lastname_input;
|
||||
let middlename_input;
|
||||
@@ -19,6 +17,7 @@
|
||||
let address_input2;
|
||||
let address_zipcode;
|
||||
let address_city;
|
||||
const dispatch = createEventDispatcher();
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
@@ -75,7 +74,7 @@
|
||||
if (processed_last_submit === true) {
|
||||
processed_last_submit = false;
|
||||
const toast = Toastify({
|
||||
text: $_('donor-is-being-added'),
|
||||
text: $_("donor-is-being-added"),
|
||||
duration: -1,
|
||||
}).showToast();
|
||||
let address = {};
|
||||
@@ -92,7 +91,7 @@
|
||||
firstname: firstname_input_value,
|
||||
lastname: lastname_input_value,
|
||||
address,
|
||||
receiptNeeded: address_checked
|
||||
receiptNeeded: address_checked,
|
||||
};
|
||||
if (middlename_input_value) {
|
||||
postdata.middlename = middlename_input_value;
|
||||
@@ -112,12 +111,11 @@
|
||||
modal_open = false;
|
||||
//
|
||||
Toastify({
|
||||
text: $_('donor-added'),
|
||||
text: $_("donor-added"),
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_donors.push(result);
|
||||
current_donors = current_donors;
|
||||
dispatch("created", { donors: [result] });
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -134,58 +132,70 @@
|
||||
{#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">
|
||||
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" />
|
||||
data-id="modal_backdrop"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
aria-hidden="true">​</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">
|
||||
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">
|
||||
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" />
|
||||
height="24"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z" /></svg>
|
||||
d="M9.33 11.5h2.17A4.5 4.5 0 0 1 16 16H8.999L9 17h8v-1a5.578 5.578 0 0 0-.886-3H19a5 5 0 0 1 4.516 2.851C21.151 18.972 17.322 21 13 21c-2.761 0-5.1-.59-7-1.625L6 10.071A6.967 6.967 0 0 1 9.33 11.5zM5 19a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v9zM18 5a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm-7-3a3 3 0 1 1 0 6 3 3 0 0 1 0-6z"
|
||||
/></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-donor')}
|
||||
{$_("create-a-new-donor")}
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
{$_('please-provide-the-nessecary-information-to-add-a-new-donor')}
|
||||
{$_(
|
||||
"please-provide-the-nessecary-information-to-add-a-new-donor"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-6 gap-6">
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="firstname"
|
||||
class="block text-sm font-medium text-gray-700">{$_('first-name')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("first-name")}</label
|
||||
>
|
||||
<input
|
||||
use:focus
|
||||
autocomplete="off"
|
||||
placeholder={$_('first-name')}
|
||||
placeholder={$_("first-name")}
|
||||
class:border-red-500={!isFirstnameValid}
|
||||
class:focus:border-red-500={!isFirstnameValid}
|
||||
class:focus:ring-red-500={!isFirstnameValid}
|
||||
@@ -193,34 +203,41 @@
|
||||
bind:this={firstname_input}
|
||||
type="text"
|
||||
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}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('first-name-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("first-name-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="trackname"
|
||||
class="block text-sm font-medium text-gray-700">{$_('middle-name')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("middle-name")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('middle-name')}
|
||||
placeholder={$_("middle-name")}
|
||||
bind:value={middlename_input_value}
|
||||
bind:this={middlename_input}
|
||||
type="text"
|
||||
name="trackname"
|
||||
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 class="col-span-6">
|
||||
<label
|
||||
for="lastname"
|
||||
class="block text-sm font-medium text-gray-700">{$_('last-name')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("last-name")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="{$_('last-name')}"
|
||||
placeholder={$_("last-name")}
|
||||
class:border-red-500={!isLastnameValid}
|
||||
class:focus:border-red-500={!isLastnameValid}
|
||||
class:focus:ring-red-500={!isLastnameValid}
|
||||
@@ -228,21 +245,25 @@
|
||||
bind:this={lastname_input}
|
||||
type="text"
|
||||
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}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('last-name-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("last-name-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="phone"
|
||||
class="block text-sm font-medium text-gray-700">{$_('phone')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("phone")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('phone')}
|
||||
placeholder={$_("phone")}
|
||||
class:border-red-500={!isPhoneValidOrEmpty}
|
||||
class:focus:border-red-500={!isPhoneValidOrEmpty}
|
||||
class:focus:ring-red-500={!isPhoneValidOrEmpty}
|
||||
@@ -250,21 +271,27 @@
|
||||
bind:this={phone_input}
|
||||
type="tel"
|
||||
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"
|
||||
/>
|
||||
{#if !isPhoneValidOrEmpty}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{@html $_('the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{@html $_(
|
||||
"the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number"
|
||||
)}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="email"
|
||||
class="block text-sm font-medium text-gray-700">{$_('e-mail-adress')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("e-mail-adress")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('e-mail-adress')}
|
||||
placeholder={$_("e-mail-adress")}
|
||||
class:border-red-500={!isEmailValidOrEmpty}
|
||||
class:focus:border-red-500={!isEmailValidOrEmpty}
|
||||
class:focus:ring-red-500={!isEmailValidOrEmpty}
|
||||
@@ -272,11 +299,13 @@
|
||||
bind:this={email_input}
|
||||
type="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 !isEmailValidOrEmpty}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('valid-email-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("valid-email-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -287,19 +316,22 @@
|
||||
id="comments"
|
||||
name="comments"
|
||||
type="checkbox"
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label
|
||||
for="comments"
|
||||
class="font-medium text-gray-700">{$_('receipt-needed')}</label>
|
||||
<label for="comments" class="font-medium text-gray-700"
|
||||
>{$_("receipt-needed")}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{#if address_checked === true}
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="address1"
|
||||
class="block text-sm font-medium text-gray-700">{$_('address')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("address")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Address"
|
||||
@@ -310,34 +342,41 @@
|
||||
bind:this={address_input1}
|
||||
type="text"
|
||||
name="address1"
|
||||
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 !isAddress1Valid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('address-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("address-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="address2"
|
||||
class="block text-sm font-medium text-gray-700">{$_('apartment-suite-etc')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("apartment-suite-etc")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('apartment-suite-etc')}
|
||||
placeholder={$_("apartment-suite-etc")}
|
||||
bind:value={address_input2_value}
|
||||
bind:this={address_input2}
|
||||
type="text"
|
||||
name="address2"
|
||||
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 class="col-span-6">
|
||||
<label
|
||||
for="zipcode"
|
||||
class="block text-sm font-medium text-gray-700">{$_('zip-postal-code')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("zip-postal-code")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('zip-postal-code')}
|
||||
placeholder={$_("zip-postal-code")}
|
||||
class:border-red-500={!iszipcodevalid}
|
||||
class:focus:border-red-500={!iszipcodevalid}
|
||||
class:focus:ring-red-500={!iszipcodevalid}
|
||||
@@ -345,18 +384,22 @@
|
||||
bind:this={address_zipcode}
|
||||
type="text"
|
||||
name="zipcode"
|
||||
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 !iszipcodevalid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('valid-zipcode-postal-code-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("valid-zipcode-postal-code-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="city"
|
||||
class="block text-sm font-medium text-gray-700">City</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>City</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="City"
|
||||
@@ -367,11 +410,13 @@
|
||||
bind:this={address_city}
|
||||
type="text"
|
||||
name="city"
|
||||
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 !iscityvalid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('valid-city-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("valid-city-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -386,16 +431,18 @@
|
||||
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')}
|
||||
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')}
|
||||
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>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { DonorService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
@@ -13,60 +13,61 @@
|
||||
dispatch("cancelDelete", { id: delete_donor.id });
|
||||
}
|
||||
function deleteDonor() {
|
||||
DonorService.donorControllerRemove(
|
||||
delete_donor.id,
|
||||
true
|
||||
)
|
||||
.then((resp) => {
|
||||
Toastify({
|
||||
text: $_('donor-deleted'),
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
location.replace("./");
|
||||
})
|
||||
.catch((err) => {});
|
||||
dispatch("delete", { id: delete_donor.id });
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
use:clickOutside
|
||||
on:click_outside={cancelDelete}>
|
||||
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">
|
||||
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" />
|
||||
data-id="modal_backdrop"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
aria-hidden="true">​</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">
|
||||
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="M9.33 11.5h2.17A4.5 4.5 0 0116 16H9v1h8v-1a5.58 5.58 0 00-.89-3H19a5 5 0 014.52 2.85A13.15 13.15 0 0113 21c-2.76 0-5.1-.59-7-1.63v-9.3a6.97 6.97 0 013.33 1.43zM5 19a1 1 0 01-1 1H2a1 1 0 01-1-1v-9a1 1 0 011-1h2a1 1 0 011 1v9zM18 5a3 3 0 110 6 3 3 0 010-6zm-7-3a3 3 0 110 6 3 3 0 010-6z"/></svg>
|
||||
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="M9.33 11.5h2.17A4.5 4.5 0 0116 16H9v1h8v-1a5.58 5.58 0 00-.89-3H19a5 5 0 014.52 2.85A13.15 13.15 0 0113 21c-2.76 0-5.1-.59-7-1.63v-9.3a6.97 6.97 0 013.33 1.43zM5 19a1 1 0 01-1 1H2a1 1 0 01-1-1v-9a1 1 0 011-1h2a1 1 0 011 1v9zM18 5a3 3 0 110 6 3 3 0 010-6zm-7-3a3 3 0 110 6 3 3 0 010-6z"
|
||||
/></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')}
|
||||
{$_("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'
|
||||
"do-you-want-to-delete-this-donor-with-all-related-donations"
|
||||
)}
|
||||
<br />
|
||||
{$_('all-associated-donations-will-get-deleted-as-well')}
|
||||
<br />
|
||||
{$_("all-associated-donations-will-get-deleted-as-well")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -76,14 +77,16 @@
|
||||
<button
|
||||
on:click={deleteDonor}
|
||||
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-donor-with-all-donations')}
|
||||
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-donor-with-all-donations")}
|
||||
</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-donor')}
|
||||
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-donor")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
14
src/components/donors/DonorAddress.svelte
Normal file
14
src/components/donors/DonorAddress.svelte
Normal file
@@ -0,0 +1,14 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let address;
|
||||
</script>
|
||||
|
||||
{#if !address || !address.address1}
|
||||
{$_("no-address")}
|
||||
{:else}
|
||||
{address.address1}<br />
|
||||
<!-- {address.address2 || ''}<br /> -->
|
||||
{address.postalcode}
|
||||
{address.city}
|
||||
{address.country}
|
||||
{/if}
|
29
src/components/donors/DonorDonations.svelte
Normal file
29
src/components/donors/DonorDonations.svelte
Normal file
@@ -0,0 +1,29 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let donations;
|
||||
</script>
|
||||
|
||||
{#if !donations || donations.length == 0}
|
||||
{$_('donor-has-no-associated-donations')}
|
||||
{:else}
|
||||
{#each donations as donation}
|
||||
{#if donation.responseType === "DISTANCEDONATION"}
|
||||
<a
|
||||
href="../donations/{donation.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-blue-600 text-white mr-1"
|
||||
>{donation.runner.firstname}
|
||||
{donation.runner.middlename || ""}
|
||||
{donation.runner.lastname}</a
|
||||
>
|
||||
{:else}
|
||||
<a
|
||||
href="../donations/{donation.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-700 text-white mr-1"
|
||||
>{$_("fixed-donation")}:
|
||||
{(donation.amount / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString("de-DE", { valute: "EUR" })}€</a
|
||||
>
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
@@ -5,50 +5,73 @@
|
||||
import DonorsOverview from "./DonorsOverview.svelte";
|
||||
$: current_donors = [];
|
||||
export let modal_open = false;
|
||||
let addDonors;
|
||||
</script>
|
||||
|
||||
<section class="container p-5">
|
||||
<span class="mb-1 text-3xl font-extrabold leading-tight">
|
||||
{$_('donors')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('DONOR:CREATE')}
|
||||
{$_("donors")}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("DONOR: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">
|
||||
{$_('add-donor')}
|
||||
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"
|
||||
>
|
||||
{$_("add-donor")}
|
||||
</button>
|
||||
{/if}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('DONOR:GET')}
|
||||
{#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(';');
|
||||
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();
|
||||
});
|
||||
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')}
|
||||
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>
|
||||
<DonorsOverview bind:current_donors />
|
||||
<DonorsOverview bind:current_donors bind:addDonors />
|
||||
</section>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('DONOR:CREATE')}
|
||||
<AddDonorModal bind:current_donors bind:modal_open />
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:CREATE")}
|
||||
<AddDonorModal
|
||||
on:created={(event) => {
|
||||
addDonors(event.detail.donors);
|
||||
}}
|
||||
bind:modal_open
|
||||
/>
|
||||
{/if}
|
||||
|
@@ -5,214 +5,255 @@
|
||||
import DonorsEmptyState from "./DonorsEmptyState.svelte";
|
||||
import ConfirmDonorDeletion from "./ConfirmDonorDeletion.svelte";
|
||||
import Toastify from "toastify-js";
|
||||
import TableBottom from "../shared/TableBottom.svelte";
|
||||
import {
|
||||
createSvelteTable,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
renderComponent,
|
||||
} from "@tanstack/svelte-table";
|
||||
import { writable } from "svelte/store";
|
||||
import { onMount } from "svelte";
|
||||
import InputElement from "../shared/InputElement.svelte";
|
||||
import TableHeader from "../shared/TableHeader.svelte";
|
||||
import TableActions from "../shared/TableActions.svelte";
|
||||
import DonorAddress from "./DonorAddress.svelte";
|
||||
import DonorDonations from "./DonorDonations.svelte";
|
||||
import { filterAddress, filterName } from "../shared/tablefilters";
|
||||
$: searchvalue = "";
|
||||
$: active_deletes = [];
|
||||
$: current_donations = [];
|
||||
let modal_open = false;
|
||||
let delete_donor = {};
|
||||
$: selectedDonors =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.original) || [];
|
||||
$: selected =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.index) || [];
|
||||
|
||||
$: dataLoaded = false;
|
||||
|
||||
export let current_donors = [];
|
||||
const donors_promise = DonorService.donorControllerGetAll().then((val) => {
|
||||
current_donors = val;
|
||||
export const addDonors = (donors) => {
|
||||
current_donors = current_donors.concat(...donors);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_donors,
|
||||
}));
|
||||
};
|
||||
|
||||
//Section table
|
||||
const columns = [
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: () => "id",
|
||||
filterFn: `equalsString`,
|
||||
},
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: () => $_("name"),
|
||||
cell: (info) => {
|
||||
const d = info.row.original;
|
||||
if (d.middlename) {
|
||||
return `${d.firstname} ${d.middlename} ${d.lastname}`;
|
||||
} else {
|
||||
return `${d.firstname} ${d.lastname}`;
|
||||
}
|
||||
},
|
||||
filterFn: `name`,
|
||||
},
|
||||
{
|
||||
accessorKey: "address",
|
||||
header: () => $_("contact-information"),
|
||||
cell: (info) => {
|
||||
return renderComponent(DonorAddress, { address: info.getValue() });
|
||||
},
|
||||
filterFn: `address`,
|
||||
},
|
||||
{
|
||||
accessorKey: "donations",
|
||||
header: () => $_("sponsorings"),
|
||||
cell: (info) => {
|
||||
return renderComponent(DonorDonations, { donations: info.getValue() });
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "donationAmount",
|
||||
header: () => $_("total-donation-amount"),
|
||||
cell: (info) => {
|
||||
return `${(info.getValue() / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString("de-DE", { valute: "EUR" })}€`;
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "paidDonationAmount",
|
||||
header: () => $_("total-paid-amount"),
|
||||
cell: (info) => {
|
||||
return `${(info.getValue() / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString("de-DE", { valute: "EUR" })}€`;
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "actions",
|
||||
header: () => $_("action"),
|
||||
cell: (info) => {
|
||||
return renderComponent(TableActions, {
|
||||
detailsLink: `./${info.row.original.id}`,
|
||||
deleteAction: () => {
|
||||
active_deletes = current_donors.filter(
|
||||
(r) => r.id == info.row.original.id
|
||||
);
|
||||
},
|
||||
deleteEnabled:
|
||||
store.state.jwtinfo.userdetails.permissions.includes(
|
||||
"DONOR:DELETE"
|
||||
),
|
||||
});
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
},
|
||||
];
|
||||
const options = writable({
|
||||
data: [],
|
||||
columns: columns,
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: 50,
|
||||
},
|
||||
},
|
||||
filterFns: {
|
||||
name: filterName,
|
||||
address: filterAddress,
|
||||
},
|
||||
enableRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
});
|
||||
const donation_promise = DonationService.donationControllerGetAll().then(
|
||||
(val) => {
|
||||
current_donations = val;
|
||||
}
|
||||
);
|
||||
const table = createSvelteTable(options);
|
||||
|
||||
function should_display_based_on_id(id) {
|
||||
if (searchvalue.toString().slice(-1) === "*") {
|
||||
return id.toString().startsWith(searchvalue.replace("*", ""));
|
||||
}
|
||||
return id.toString() === searchvalue;
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
let page = 0;
|
||||
while (page >= 0) {
|
||||
const donors = await DonorService.donorControllerGetAll(page, 500);
|
||||
if (donors.length == 0) {
|
||||
page = -2;
|
||||
}
|
||||
|
||||
current_donors = current_donors.concat(...donors);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_donors,
|
||||
}));
|
||||
|
||||
dataLoaded = true;
|
||||
page++;
|
||||
}
|
||||
|
||||
console.log("All donors loaded");
|
||||
});
|
||||
</script>
|
||||
|
||||
<ConfirmDonorDeletion
|
||||
on:cancelDelete={(event) => {
|
||||
modal_open = false;
|
||||
active_deletes[event.detail.id] = false;
|
||||
active_deletes = active_deletes.filter((a) => a.id !== event.detail.id);
|
||||
}}
|
||||
bind:modal_open
|
||||
bind:delete_donor />
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('DONOR:GET')}
|
||||
{#await donors_promise && donation_promise}
|
||||
on:delete={async (event) => {
|
||||
await DonorService.donorControllerRemove(event.detail.id, true);
|
||||
Toastify({
|
||||
text: $_("donor-deleted"),
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_donors = current_donors.filter((d) => d.id !== event.detail.id);
|
||||
active_deletes = active_deletes.filter((a) => a.id !== event.detail.id);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_donors,
|
||||
}));
|
||||
}}
|
||||
modal_open={active_deletes.length > 0}
|
||||
delete_donor={active_deletes[0]}
|
||||
/>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("DONOR:GET")}
|
||||
{#if !dataLoaded}
|
||||
<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">{$_('donors-are-being-loaded')}</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
role="alert"
|
||||
>
|
||||
<p class="font-bold">{$_("donors-are-being-loaded")}</p>
|
||||
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_donors.length === 0}
|
||||
<DonorsEmptyState />
|
||||
{: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">
|
||||
{$_('name')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('contact-information')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('donations')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('total-donation-amount')}
|
||||
</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">
|
||||
<span class="sr-only">{$_('action')}</span>
|
||||
{:else if current_donors.length === 0}
|
||||
<DonorsEmptyState />
|
||||
{:else}
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_("datatable.search")}
|
||||
aria-label={$_("datatable.search")}
|
||||
class="mb-4"
|
||||
/>
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
|
||||
>
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
{#each $table.getHeaderGroups() as headerGroup}
|
||||
<tr class="select-none">
|
||||
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={$table.getIsAllRowsSelected()}
|
||||
indeterminate={$table.getIsSomeRowsSelected()}
|
||||
on:change={() => $table.toggleAllRowsSelected()}
|
||||
/>
|
||||
</th>
|
||||
{#each headerGroup.headers as header}
|
||||
<TableHeader {header} />
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
{#each current_donors as donor}
|
||||
{#if donor.firstname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || donor.lastname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || should_display_based_on_id(donor.id)}
|
||||
<tr data-rowid="donor_{donor.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">
|
||||
{donor.firstname}
|
||||
{donor.middlename || ''}
|
||||
{donor.lastname}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if donor.email}
|
||||
<div class="text-sm text-gray-500">{donor.email}</div>
|
||||
{/if}
|
||||
{#if donor.phone}
|
||||
<div class="text-sm text-gray-500">{donor.phone}</div>
|
||||
{/if}
|
||||
{#if donor.address.address1 !== null}
|
||||
{donor.address.address1}<br />
|
||||
{donor.address.address2 || ''}<br />
|
||||
{donor.address.postalcode}
|
||||
{donor.address.city}
|
||||
{donor.address.country}
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if current_donations.filter((d) => d.donor.id == donor.id).length > 0}
|
||||
{#each current_donations.filter((o) => o.donor.id == donor.id) as d}
|
||||
{#if d.responseType === 'DISTANCEDONATION'}
|
||||
<a
|
||||
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}
|
||||
{d.runner.middlename || ''}
|
||||
{d.runner.lastname}</a>
|
||||
{:else}
|
||||
<a
|
||||
href="../donations/{d.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-700 text-white mr-1">{$_('fixed-donation')}:
|
||||
{(d.amount / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString('de-DE', { valute: 'EUR' })}€</a>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else}{$_('donor-has-no-associated-donations')}{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{(donor.donationAmount / 100)
|
||||
.toFixed(2)
|
||||
.toLocaleString('de-DE', { valute: 'EUR' })}€
|
||||
</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}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[donor.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
DonorService.donorControllerRemove(donor.id, false)
|
||||
.then((resp) => {
|
||||
current_donors = current_donors.filter((obj) => obj.id !== donor.id);
|
||||
Toastify({
|
||||
text: 'Donor deleted',
|
||||
duration: 500,
|
||||
backgroundColor:
|
||||
'linear-gradient(to right, #00b09b, #96c93d)',
|
||||
}).showToast();
|
||||
})
|
||||
.catch((err) => {
|
||||
modal_open = true;
|
||||
delete_donor = donor;
|
||||
});
|
||||
}}
|
||||
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="./{donor.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('DONOR:DELETE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[donor.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>
|
||||
{/each}
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each $table.getRowModel().rows as row}
|
||||
<tr>
|
||||
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={row.getIsSelected()}
|
||||
on:change={() => row.toggleSelected()}
|
||||
/>
|
||||
</td>
|
||||
{#each row.getVisibleCells() as cell}
|
||||
<td>
|
||||
<svelte:component
|
||||
this={flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
<div class="h-2" />
|
||||
<TableBottom {table} {selected} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
export let modal_open;
|
||||
(function () {
|
||||
document.onkeydown = function (e) {
|
||||
@@ -25,7 +25,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
import { _, getLocaleFromNavigator } from "svelte-i18n";
|
||||
import marked from "marked";
|
||||
import Footer from "./Footer.svelte";
|
||||
import * as css from "../base/simple.css";
|
||||
// import * as css from "../base/simple.css?inline";
|
||||
let html = "";
|
||||
async function load() {
|
||||
let md = await fetch("/privacy_" + getLocaleFromNavigator() + ".md");
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import Toastify from "toastify-js";
|
||||
import { UserGroupService } from "@odit/lfk-client-js";
|
||||
export let modal_open;
|
||||
@@ -69,7 +69,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -30,7 +30,7 @@
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
class="mb-4" />
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||
<table class="divide-y divide-gray-200 w-full">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { RunnerOrganizationService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
export let modal_open;
|
||||
@@ -89,7 +89,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { RunnerOrganizationService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
@@ -32,7 +32,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={cancelDelete}>
|
||||
<div
|
||||
|
@@ -119,9 +119,9 @@
|
||||
}
|
||||
}
|
||||
async function copy() {
|
||||
if(!editable.registrationKey){
|
||||
if (!editable.registrationKey) {
|
||||
Toastify({
|
||||
text: $_('you-have-to-save-your-changes-to-generate-a-link'),
|
||||
text: $_("you-have-to-save-your-changes-to-generate-a-link"),
|
||||
duration: 500,
|
||||
backgroundColor:
|
||||
"linear-gradient(90deg, hsla(281, 37%, 45%, 1) 0%, hsla(1, 62%, 48%, 1) 100%)",
|
||||
@@ -167,7 +167,8 @@
|
||||
passed_orgs={[]}
|
||||
passed_org={editable}
|
||||
opened_from="OrgDetail"
|
||||
bind:import_modal_open />
|
||||
bind:import_modal_open
|
||||
/>
|
||||
<ConfirmOrgDeletion bind:modal_open bind:delete_org />
|
||||
{#if data_loaded}
|
||||
<section class="container p-5">
|
||||
@@ -176,29 +177,35 @@
|
||||
<span data-id="org_actions_${editable.id}">
|
||||
<GenerateSponsoringContracts
|
||||
bind:sponsoring_contracts_show
|
||||
bind:generate_orgs />
|
||||
bind:generate_orgs
|
||||
/>
|
||||
<GenerateRunnerCards bind:cards_show bind:generate_orgs />
|
||||
<GenerateRunnerCertificates bind:certificates_show bind:generate_orgs />
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:IMPORT')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:IMPORT")}
|
||||
<button
|
||||
on:click={() => {
|
||||
import_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">
|
||||
{$_('import-runners')}
|
||||
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"
|
||||
>
|
||||
{$_("import-runners")}
|
||||
</button>
|
||||
{/if}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:DELETE')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:DELETE")}
|
||||
{#if delete_triggered}
|
||||
<button
|
||||
on:click={deleteOrganization}
|
||||
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-delete')}</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"
|
||||
>{$_("confirm-delete")}</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>
|
||||
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
|
||||
@@ -206,7 +213,9 @@
|
||||
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-organization')}</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-organization")}</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !delete_triggered}
|
||||
@@ -215,7 +224,9 @@
|
||||
disabled={!save_enabled}
|
||||
class:opacity-50={!save_enabled}
|
||||
type="button"
|
||||
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>
|
||||
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
|
||||
>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
@@ -234,12 +245,13 @@
|
||||
class="h-3 w-3 stroke-current"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"><path
|
||||
d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
<polyline points="9 22 9 12 15 12 15 22" /></svg>
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
<polyline points="9 22 9 12 15 12 15 22" /></svg
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="/">{$_('home')}</a><svg
|
||||
<a class="mr-2" href="/">{$_("home")}</a><svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
@@ -249,24 +261,25 @@
|
||||
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>
|
||||
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="mr-2 flex items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"><path fill="none" d="M0 0h24v24H0z" />
|
||||
height="24"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z" /></svg>
|
||||
d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z"
|
||||
/></svg
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="./">{$_('organizations')}</a><svg
|
||||
<a class="mr-2" href="./">{$_("organizations")}</a><svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
@@ -276,12 +289,10 @@
|
||||
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>
|
||||
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">Org-Details #{params.orgid}</span>
|
||||
@@ -291,81 +302,88 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
<label for="name" class="font-medium text-gray-700">{$_('name')}</label>
|
||||
<label for="name" class="font-medium text-gray-700">{$_("name")}</label>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('name')}
|
||||
placeholder={$_("name")}
|
||||
type="text"
|
||||
bind:value={editable.name}
|
||||
name="name"
|
||||
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 class="text-sm w-full">
|
||||
<label
|
||||
for="contact"
|
||||
class="font-medium text-gray-700">{$_('contact')}</label>
|
||||
<label for="contact" class="font-medium text-gray-700"
|
||||
>{$_("contact")}</label
|
||||
>
|
||||
<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"
|
||||
itemFilter={(label, filterText, option) => label
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
filterText.toLowerCase()
|
||||
) || option.value.id
|
||||
.toString()
|
||||
.startsWith(filterText.toLowerCase())}
|
||||
itemFilter={(label, filterText, option) =>
|
||||
label.toLowerCase().includes(filterText.toLowerCase()) ||
|
||||
option.value.id.toString().startsWith(filterText.toLowerCase())}
|
||||
items={contacts}
|
||||
showChevron={true}
|
||||
placeholder={$_('no-contact-selected')}
|
||||
noOptionsMessage={$_('no-contact-found')}
|
||||
placeholder={$_("no-contact-selected")}
|
||||
noOptionsMessage={$_("no-contact-found")}
|
||||
bind:selectedValue={contact}
|
||||
on:select={(selectedValue) => (editable.contact = selectedValue.detail.value)}
|
||||
on:clear={() => (editable.contact = null)} />
|
||||
on:select={(selectedValue) =>
|
||||
(editable.contact = selectedValue.detail.value)}
|
||||
on:clear={() => (editable.contact = null)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex items-start mt-2">
|
||||
<div class="flex items-center h-5">
|
||||
<input
|
||||
bind:checked={editable.registrationEnabled}
|
||||
id="comments"
|
||||
name="comments"
|
||||
id="toggle_selfservice_feature"
|
||||
name="toggle_selfservice_feature"
|
||||
type="checkbox"
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label
|
||||
for="comments"
|
||||
class="font-medium text-gray-700">{$_('selfservice-registration')}</label>
|
||||
for="toggle_selfservice_feature"
|
||||
class="font-medium text-gray-700"
|
||||
>{$_("selfservice-registration")}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{#if editable.registrationEnabled}
|
||||
<div class="text-sm w-full">
|
||||
<div on:click={copy} class="inline-flex w-full">
|
||||
<button on:click={copy} class="inline-flex w-full">
|
||||
<p
|
||||
name="token"
|
||||
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 p-2">
|
||||
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 p-2"
|
||||
>
|
||||
{#if editable.registrationKey}
|
||||
{registrationLink}
|
||||
{registrationLink}
|
||||
{:else}
|
||||
{$_('you-have-to-save-your-changes-to-generate-a-link')}
|
||||
{$_("you-have-to-save-your-changes-to-generate-a-link")}
|
||||
{/if}
|
||||
</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">
|
||||
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" />
|
||||
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>
|
||||
d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z"
|
||||
/></svg
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{#if editable.registrationKey}
|
||||
<p class="text-gray-500 text-xs">
|
||||
{$_('click-to-copy-the-link-into-your-clipboard')}
|
||||
</p>
|
||||
<p class="text-gray-500 text-xs">
|
||||
{$_("click-to-copy-the-link-into-your-clipboard")}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -375,15 +393,17 @@
|
||||
<div class="flex items-center h-5">
|
||||
<input
|
||||
bind:checked={editable.address_checked}
|
||||
id="comments"
|
||||
name="comments"
|
||||
id="toggle_address_checkbox"
|
||||
name="toggle_address_checkbox"
|
||||
type="checkbox"
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
|
||||
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-3 text-sm">
|
||||
<label
|
||||
for="comments"
|
||||
class="font-medium text-gray-700">{$_('address')}</label>
|
||||
for="toggle_address_checkbox"
|
||||
class="font-medium text-gray-700">{$_("address")}</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -391,7 +411,9 @@
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="address1"
|
||||
class="block text-sm font-medium text-gray-700">{$_('address')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("address")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Address"
|
||||
@@ -401,65 +423,74 @@
|
||||
bind:value={editable.address.address1}
|
||||
type="text"
|
||||
name="address1"
|
||||
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 !isAddress1Valid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('address-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("address-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="address2"
|
||||
class="block text-sm font-medium text-gray-700">{$_('apartment-suite-etc')}</label>
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("apartment-suite-etc")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('apartment-suite-etc')}
|
||||
placeholder={$_("apartment-suite-etc")}
|
||||
bind:value={editable.address.address2}
|
||||
type="text"
|
||||
name="address2"
|
||||
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 class="col-span-6">
|
||||
<label
|
||||
for="zipcode"
|
||||
class="block text-sm font-medium text-gray-700">{$_('zip-postal-code')}</label>
|
||||
<label for="zipcode" class="block text-sm font-medium text-gray-700"
|
||||
>{$_("zip-postal-code")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('zip-postal-code')}
|
||||
placeholder={$_("zip-postal-code")}
|
||||
class:border-red-500={!iszipcodevalid}
|
||||
class:focus:border-red-500={!iszipcodevalid}
|
||||
class:focus:ring-red-500={!iszipcodevalid}
|
||||
bind:value={editable.address.postalcode}
|
||||
type="text"
|
||||
name="zipcode"
|
||||
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 !iszipcodevalid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('valid-zipcode-postal-code-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("valid-zipcode-postal-code-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="city"
|
||||
class="block text-sm font-medium text-gray-700">{$_('city')}</label>
|
||||
<label for="city" class="block text-sm font-medium text-gray-700"
|
||||
>{$_("city")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('city')}
|
||||
placeholder={$_("city")}
|
||||
class:border-red-500={!iscityvalid}
|
||||
class:focus:border-red-500={!iscityvalid}
|
||||
class:focus:ring-red-500={!iscityvalid}
|
||||
bind:value={editable.address.city}
|
||||
type="text"
|
||||
name="city"
|
||||
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 !iscityvalid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('valid-city-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("valid-city-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -469,7 +500,7 @@
|
||||
</section>
|
||||
{:else}
|
||||
{#await promise}
|
||||
{$_('organization-detail-is-being-loaded')}
|
||||
{$_("organization-detail-is-being-loaded")}
|
||||
{:catch error}
|
||||
<PromiseError />
|
||||
{/await}
|
||||
|
@@ -51,7 +51,7 @@
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
class="mb-4" />
|
||||
<div class="h-12">
|
||||
<GenerateSponsoringContracts
|
||||
bind:sponsoring_contracts_show
|
||||
@@ -132,7 +132,7 @@
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{#if o.address.address1 !== null}
|
||||
{o.address.address1}<br />
|
||||
{o.address.address2 || ''}<br />
|
||||
<!-- {o.address.address2 || ''}<br /> -->
|
||||
{o.address.postalcode}
|
||||
{o.address.city}
|
||||
{o.address.country}
|
||||
|
@@ -1,403 +1,418 @@
|
||||
<script>
|
||||
import { getLocaleFromNavigator, _ } from "svelte-i18n";
|
||||
import {
|
||||
RunnerCardService,
|
||||
RunnerOrganizationService,
|
||||
RunnerTeamService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
export let cards_show = false;
|
||||
export let generate_cards = [];
|
||||
export let generate_runners = [];
|
||||
export let generate_orgs = [];
|
||||
export let generate_teams = [];
|
||||
$: cards_dropdown_open = false;
|
||||
document.addEventListener("click", function (e) {
|
||||
if (
|
||||
e.target.parentNode?.parentNode?.id != "cards:dropdown" &&
|
||||
e.target.parentNode?.parentNode?.id != "cards:dropdown:menu"
|
||||
) {
|
||||
cards_dropdown_open = false;
|
||||
}
|
||||
});
|
||||
import { getLocaleFromNavigator, _ } from "svelte-i18n";
|
||||
import {
|
||||
RunnerCardService,
|
||||
RunnerOrganizationService,
|
||||
RunnerTeamService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { init } from "@paralleldrive/cuid2";
|
||||
const createId = init({ length: 10, fingerprint: "lfk-frontend" });
|
||||
|
||||
function generateRunnerCards(locale) {
|
||||
cards_dropdown_open = false;
|
||||
export let cards_show = false;
|
||||
export let generate_cards = [];
|
||||
export let generate_runners = [];
|
||||
export let generate_orgs = [];
|
||||
export let generate_teams = [];
|
||||
$: cards_dropdown_open = false;
|
||||
document.addEventListener("click", function (e) {
|
||||
if (
|
||||
e.target.parentNode?.parentNode?.id != "cards:dropdown" &&
|
||||
e.target.parentNode?.parentNode?.id != "cards:dropdown:menu"
|
||||
) {
|
||||
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);
|
||||
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 {
|
||||
generateCards(locale);
|
||||
return response.blob();
|
||||
}
|
||||
}
|
||||
|
||||
function generateCards(locale) {
|
||||
const toast = Toastify({
|
||||
text: $_("generating-pdf"),
|
||||
duration: -1,
|
||||
})
|
||||
.then((blob) => {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
let a = document.createElement("a");
|
||||
a.href = url;
|
||||
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();
|
||||
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);
|
||||
let a = document.createElement("a");
|
||||
a.href = url;
|
||||
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) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
async function generateRunnersCards(locale) {
|
||||
const toast = Toastify({
|
||||
text: $_("generating-pdf"),
|
||||
duration: -1,
|
||||
async function generateRunnersCards(locale) {
|
||||
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_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();
|
||||
const current_cards = await RunnerCardService.runnerCardControllerGetAll();
|
||||
})
|
||||
.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 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);
|
||||
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),
|
||||
}
|
||||
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;
|
||||
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_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}.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}.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) => {
|
||||
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}_${t.name}-${locale}.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) => {});
|
||||
}
|
||||
}
|
||||
.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}_${
|
||||
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) => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if cards_show}
|
||||
<div id="cards:dropdown" class="relative inline-block">
|
||||
<div>
|
||||
<button
|
||||
on:click={() => {
|
||||
cards_dropdown_open = !cards_dropdown_open;
|
||||
}}
|
||||
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"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
{$_('generate-runnercards')}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="-mr-1 ml-2 h-5 w-5"><path
|
||||
fill="none"
|
||||
d="M0 0h24v24H0z" />
|
||||
<path
|
||||
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>
|
||||
</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"
|
||||
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 id="cards:dropdown" class="relative inline-block">
|
||||
<div>
|
||||
<button
|
||||
on:click={() => {
|
||||
cards_dropdown_open = !cards_dropdown_open;
|
||||
}}
|
||||
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"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
>
|
||||
{$_("generate-runnercards")}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="-mr-1 ml-2 h-5 w-5"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
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
|
||||
>
|
||||
</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>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
@@ -1,332 +1,355 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import {
|
||||
DonationService,
|
||||
RunnerTeamService,
|
||||
RunnerOrganizationService
|
||||
} from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
export let certificates_show = false;
|
||||
export let generate_runners = [];
|
||||
export let generate_orgs = [];
|
||||
export let generate_teams = [];
|
||||
$: certificates_dropdown_open = false;
|
||||
document.addEventListener("click", function (e) {
|
||||
if (
|
||||
e.target.parentNode?.parentNode?.id != "certificates:dropdown" &&
|
||||
e.target.parentNode?.parentNode?.id != "certificates:dropdown:menu"
|
||||
) {
|
||||
certificates_dropdown_open = false;
|
||||
}
|
||||
});
|
||||
import { _ } from "svelte-i18n";
|
||||
import {
|
||||
DonationService,
|
||||
RunnerTeamService,
|
||||
RunnerOrganizationService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { init } from "@paralleldrive/cuid2";
|
||||
const createId = init({ length: 10, fingerprint: "lfk-frontend" });
|
||||
|
||||
function generateCertificates(locale) {
|
||||
certificates_dropdown_open = false;
|
||||
export let certificates_show = false;
|
||||
export let generate_runners = [];
|
||||
export let generate_orgs = [];
|
||||
export let generate_teams = [];
|
||||
$: certificates_dropdown_open = false;
|
||||
document.addEventListener("click", function (e) {
|
||||
if (
|
||||
e.target.parentNode?.parentNode?.id != "certificates:dropdown" &&
|
||||
e.target.parentNode?.parentNode?.id != "certificates:dropdown:menu"
|
||||
) {
|
||||
certificates_dropdown_open = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (generate_orgs.length > 0) {
|
||||
generateOrgCertificates(locale);
|
||||
} else if (generate_teams.length > 0) {
|
||||
generateTeamCertificates(locale);
|
||||
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 {
|
||||
generateRunnerCertificates(locale);
|
||||
return response.blob();
|
||||
}
|
||||
}
|
||||
|
||||
async function generateRunnerCertificates(locale) {
|
||||
const toast = Toastify({
|
||||
text: $_("generating-pdf"),
|
||||
duration: -1,
|
||||
})
|
||||
.then((blob) => {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
let a = document.createElement("a");
|
||||
a.href = url;
|
||||
if (generate_runners.length == 1) {
|
||||
a.download = `${$_("certificates")}_${
|
||||
generate_runners[0].firstname
|
||||
}_${generate_runners[0].lastname}-${locale}-${createId()}.pdf`;
|
||||
} else {
|
||||
a.download = `${$_("certificates")}-${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();
|
||||
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 = [];
|
||||
for (let runner of generate_runners) {
|
||||
runner.distanceDonations = current_donations.filter((d) => d.runner?.id == runner.id) || [];
|
||||
console.log(runner.distanceDonations)
|
||||
certificateRunners.push(runner);
|
||||
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),
|
||||
}
|
||||
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;
|
||||
if(generate_runners.length == 1){
|
||||
a.download = `${$_('certificates')}_${generate_runners[0].firstname}_${generate_runners[0].lastname}-${locale}.pdf`;
|
||||
}
|
||||
else{
|
||||
a.download = `${$_('certificates')}-${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 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}.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}_direct-${locale}.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 = [];
|
||||
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}_${t.name}-${locale}.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) => {});
|
||||
}
|
||||
}
|
||||
.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}_${
|
||||
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) => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if certificates_show}
|
||||
<div id="certificates:dropdown" class="relative inline-block">
|
||||
<div>
|
||||
<button
|
||||
on:click={() => {
|
||||
certificates_dropdown_open = !certificates_dropdown_open;
|
||||
}}
|
||||
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"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
{$_('generate-runner-certificates')}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="-mr-1 ml-2 h-5 w-5"><path
|
||||
fill="none"
|
||||
d="M0 0h24v24H0z" />
|
||||
<path
|
||||
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>
|
||||
</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"
|
||||
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 id="certificates:dropdown" class="relative inline-block">
|
||||
<div>
|
||||
<button
|
||||
on:click={() => {
|
||||
certificates_dropdown_open = !certificates_dropdown_open;
|
||||
}}
|
||||
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"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
>
|
||||
{$_("generate-runner-certificates")}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="-mr-1 ml-2 h-5 w-5"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
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
|
||||
>
|
||||
</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>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
@@ -1,306 +1,324 @@
|
||||
<script>
|
||||
import { getLocaleFromNavigator, _ } from "svelte-i18n";
|
||||
import {
|
||||
RunnerOrganizationService,
|
||||
RunnerTeamService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
export let sponsoring_contracts_show = false;
|
||||
export let generate_runners = [];
|
||||
export let generate_orgs = [];
|
||||
export let generate_teams = [];
|
||||
$: sponsoring_contracts_download_open = false;
|
||||
document.addEventListener("click", function (e) {
|
||||
if (
|
||||
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown" &&
|
||||
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown:menu"
|
||||
) {
|
||||
sponsoring_contracts_download_open = false;
|
||||
}
|
||||
});
|
||||
import { getLocaleFromNavigator, _ } from "svelte-i18n";
|
||||
import {
|
||||
RunnerOrganizationService,
|
||||
RunnerTeamService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { init } from "@paralleldrive/cuid2";
|
||||
const createId = init({ length: 10, fingerprint: "lfk-frontend" });
|
||||
|
||||
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);
|
||||
}
|
||||
export let sponsoring_contracts_show = false;
|
||||
export let generate_runners = [];
|
||||
export let generate_orgs = [];
|
||||
export let generate_teams = [];
|
||||
$: sponsoring_contracts_download_open = false;
|
||||
document.addEventListener("click", function (e) {
|
||||
if (
|
||||
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown" &&
|
||||
e.target.parentNode?.parentNode?.id != "sponsoring:dropdown:menu"
|
||||
) {
|
||||
sponsoring_contracts_download_open = false;
|
||||
}
|
||||
});
|
||||
|
||||
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}.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) => {});
|
||||
}
|
||||
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 generateOrgContracts(locale) {
|
||||
const toast = Toastify({
|
||||
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}.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}.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) => {});
|
||||
}
|
||||
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) => {});
|
||||
}
|
||||
}
|
||||
|
||||
function generateRunnerContracts(locale) {
|
||||
const toast = Toastify({
|
||||
text: $_("generating-pdf"),
|
||||
duration: -1,
|
||||
}).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),
|
||||
}
|
||||
async function generateOrgContracts(locale) {
|
||||
const toast = Toastify({
|
||||
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;
|
||||
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);
|
||||
});
|
||||
.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) {
|
||||
const toast = Toastify({
|
||||
text: $_("generating-pdf"),
|
||||
duration: -1,
|
||||
}).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 {
|
||||
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}-${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();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if sponsoring_contracts_show}
|
||||
<div id="sponsoring:dropdown" class="relative inline-block">
|
||||
<div>
|
||||
<button
|
||||
on:click={() => {
|
||||
sponsoring_contracts_download_open = !sponsoring_contracts_download_open;
|
||||
}}
|
||||
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"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
{$_('generate-sponsoring-contracts')}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="-mr-1 ml-2 h-5 w-5"><path
|
||||
fill="none"
|
||||
d="M0 0h24v24H0z" />
|
||||
<path
|
||||
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>
|
||||
</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"
|
||||
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 id="sponsoring:dropdown" class="relative inline-block">
|
||||
<div>
|
||||
<button
|
||||
on:click={() => {
|
||||
sponsoring_contracts_download_open =
|
||||
!sponsoring_contracts_download_open;
|
||||
}}
|
||||
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"
|
||||
id="options-menu"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="true"
|
||||
>
|
||||
{$_("generate-sponsoring-contracts")}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
class="-mr-1 ml-2 h-5 w-5"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
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
|
||||
>
|
||||
</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>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
@@ -1,30 +0,0 @@
|
||||
<script>
|
||||
import { _, json } from "svelte-i18n";
|
||||
import { getlang } from "./datatable_i18n";
|
||||
import { Grid } from "gridjs";
|
||||
//
|
||||
let table;
|
||||
const datatable = new Grid({
|
||||
columns: ["Name", "Email", "Phone Number"],
|
||||
language: getlang($json("datatable")),
|
||||
sort: true,
|
||||
search: { enabled: true },
|
||||
data: [
|
||||
["John", "john@example.com", "(353) 01 222 3333"],
|
||||
["Mark", "mark@gmail.com", "(01) 22 888 4444"],
|
||||
["Eoin", "eoin@gmail.com", "0097 22 654 00033"],
|
||||
["Sarah", "sarahcdd@gmail.com", "+322 876 1233"],
|
||||
["Afshin", "afshin@mail.com", "(353) 22 87 8356"],
|
||||
],
|
||||
pagination: {
|
||||
enabled: true,
|
||||
limit: 2,
|
||||
summary: false,
|
||||
},
|
||||
});
|
||||
setTimeout(() => {
|
||||
datatable.render(table);
|
||||
}, 0);
|
||||
</script>
|
||||
|
||||
<div bind:this={table} />
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import {
|
||||
RunnerService,
|
||||
RunnerTeamService,
|
||||
@@ -11,8 +11,10 @@
|
||||
import isMobilePhone from "validator/es/lib/isMobilePhone";
|
||||
import Toastify from "toastify-js";
|
||||
import Select from "svelte-select";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
export let modal_open;
|
||||
export let current_runners;
|
||||
$: selected_team = undefined;
|
||||
let firstname_input;
|
||||
let lastname_input;
|
||||
@@ -107,8 +109,7 @@
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_runners.push(result);
|
||||
current_runners = current_runners;
|
||||
dispatch("created", { runners: [result] });
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -125,7 +126,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
115
src/components/runners/DeleteRunnerModal.svelte
Normal file
115
src/components/runners/DeleteRunnerModal.svelte
Normal file
@@ -0,0 +1,115 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
export let modal_open;
|
||||
export let delete_runner = {
|
||||
id: 0,
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
};
|
||||
const dispatch = createEventDispatcher();
|
||||
onMount(() => {
|
||||
document.onkeydown = (e) => {
|
||||
e = e || window.event;
|
||||
if (e.key === "Escape") {
|
||||
modal_open = false;
|
||||
}
|
||||
if (e.keyCode === 13) {
|
||||
if (createbtnenabled === true) {
|
||||
createbtnenabled = false;
|
||||
submit();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
async function submit() {
|
||||
dispatch("delete", { id: delete_runner.id });
|
||||
modal_open=false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
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">​</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
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="h-6 w-6 text-blue-600"
|
||||
fill="currentColor"
|
||||
width="24"
|
||||
height="24"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<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"
|
||||
/></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">
|
||||
{$_("confirm-delete")}
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
{$_("please-confirm-the-deletion-of-runner")}
|
||||
</p>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<span class="inline-block"
|
||||
>{delete_runner.firstname} {delete_runner.lastname}</span
|
||||
>
|
||||
</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={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"
|
||||
>
|
||||
{$_("delete")}
|
||||
</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}
|
@@ -3,7 +3,7 @@
|
||||
import { read as readXlsx, utils as xlsx_utils } from "xlsx";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import Toastify from "toastify-js";
|
||||
import {
|
||||
ImportService,
|
||||
@@ -16,7 +16,6 @@
|
||||
export let passed_org;
|
||||
export let passed_orgs;
|
||||
export let passed_team;
|
||||
export let current_runners;
|
||||
export let import_modal_open;
|
||||
$: searchvalue = "";
|
||||
$: importButtonEnabled =
|
||||
@@ -169,7 +168,7 @@
|
||||
mapped
|
||||
)
|
||||
.then((resp) => {
|
||||
current_runners = current_runners.concat(resp);
|
||||
dispatch("created", { runners: resp });
|
||||
toast.hideToast();
|
||||
recent_processed = true;
|
||||
Toastify({
|
||||
@@ -198,7 +197,7 @@
|
||||
mapped
|
||||
)
|
||||
.then((resp) => {
|
||||
current_runners = current_runners.concat(resp);
|
||||
dispatch("created", { runners: resp });
|
||||
toast.hideToast();
|
||||
recent_processed = true;
|
||||
Toastify({
|
||||
@@ -228,7 +227,7 @@
|
||||
{#if import_modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
cancelModal();
|
||||
|
@@ -71,6 +71,9 @@
|
||||
}).showToast();
|
||||
let postdata = {};
|
||||
postdata = Object.assign(postdata, editable);
|
||||
if (postdata.phone === "") {
|
||||
postdata.phone = null;
|
||||
}
|
||||
RunnerService.runnerControllerPut(original_data.id, postdata)
|
||||
.then((resp) => {
|
||||
Object.assign(original_data, editable);
|
||||
@@ -95,7 +98,7 @@
|
||||
</script>
|
||||
|
||||
{#await runner_promise}
|
||||
{$_('loading-runners')}
|
||||
{$_("loading-runners")}
|
||||
{:then}
|
||||
<section class="container p-5 select-none">
|
||||
<div class="flex flex-row mb-4">
|
||||
@@ -109,12 +112,15 @@
|
||||
class="flex-shrink-0 w-5 h-5 mr-2"
|
||||
fill="currentColor"
|
||||
width="24"
|
||||
height="24"><path fill="none" d="M0 0h24v24H0z" />
|
||||
height="24"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<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" /></svg>
|
||||
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
|
||||
>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="./">{$_('runners')}</a><svg
|
||||
<a class="mr-2" href="./">{$_("runners")}</a><svg
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
stroke-width="2"
|
||||
@@ -124,17 +130,17 @@
|
||||
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>
|
||||
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.firstname}
|
||||
{original_data.middlename || ''}
|
||||
{original_data.lastname}</span>
|
||||
<span class="mr-2"
|
||||
>{original_data.firstname}
|
||||
{original_data.middlename || ""}
|
||||
{original_data.lastname}</span
|
||||
>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
@@ -142,36 +148,42 @@
|
||||
</div>
|
||||
<div class="mb-8 text-3xl font-extrabold leading-tight">
|
||||
{original_data.firstname}
|
||||
{original_data.middlename || ''}
|
||||
{original_data.middlename || ""}
|
||||
{original_data.lastname}
|
||||
<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}
|
||||
<button
|
||||
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">{$_('confirm-deletion')}</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"
|
||||
>{$_("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>
|
||||
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}
|
||||
<GenerateSponsoringContracts
|
||||
bind:sponsoring_contracts_show
|
||||
bind:generate_runners />
|
||||
<GenerateRunnerCards
|
||||
bind:cards_show
|
||||
bind:generate_runners />
|
||||
bind:generate_runners
|
||||
/>
|
||||
<GenerateRunnerCards bind:cards_show bind:generate_runners />
|
||||
<GenerateRunnerCertificates
|
||||
bind:certificates_show
|
||||
bind:generate_runners />
|
||||
bind:generate_runners
|
||||
/>
|
||||
{#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-runner')}</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-runner")}</button
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !delete_triggered}
|
||||
@@ -180,121 +192,128 @@
|
||||
class:opacity-50={!save_enabled}
|
||||
type="button"
|
||||
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">{$_('save-changes')}</button>
|
||||
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
|
||||
>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
<!-- -->
|
||||
<div class="text-sm w-full">
|
||||
<label
|
||||
for="firstname"
|
||||
class="font-medium text-gray-700">{$_('first-name')}</label>
|
||||
<label for="firstname" class="font-medium text-gray-700"
|
||||
>{$_("first-name")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('first-name')}
|
||||
placeholder={$_("first-name")}
|
||||
type="text"
|
||||
class:border-red-500={!isFirstnameValid}
|
||||
class:focus:border-red-500={!isFirstnameValid}
|
||||
class:focus:ring-red-500={!isFirstnameValid}
|
||||
bind:value={editable.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}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('first-name-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("first-name-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
<label
|
||||
for="middlename"
|
||||
class="font-medium text-gray-700">{$_('middle-name')}</label>
|
||||
<label for="middlename" class="font-medium text-gray-700"
|
||||
>{$_("middle-name")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('middle-name')}
|
||||
placeholder={$_("middle-name")}
|
||||
type="text"
|
||||
bind:value={editable.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 class="text-sm w-full">
|
||||
<label
|
||||
for="lastname"
|
||||
class="font-medium text-gray-700">{$_('last-name')}</label>
|
||||
<label for="lastname" class="font-medium text-gray-700"
|
||||
>{$_("last-name")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('last-name')}
|
||||
placeholder={$_("last-name")}
|
||||
type="text"
|
||||
bind:value={editable.lastname}
|
||||
class:border-red-500={!isLastnameValid}
|
||||
class:focus:border-red-500={!isLastnameValid}
|
||||
class:focus:ring-red-500={!isLastnameValid}
|
||||
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}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('last-name-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("last-name-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
<label
|
||||
for="email"
|
||||
class="font-medium text-gray-700">{$_('e-mail-adress')}</label>
|
||||
<label for="email" class="font-medium text-gray-700"
|
||||
>{$_("e-mail-adress")}</label
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('e-mail-adress')}
|
||||
placeholder={$_("e-mail-adress")}
|
||||
type="email"
|
||||
bind:value={editable.email}
|
||||
class:border-red-500={!isEmailValid}
|
||||
class:focus:border-red-500={!isEmailValid}
|
||||
class:focus:ring-red-500={!isEmailValid}
|
||||
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}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
{$_('valid-email-is-required')}
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1"
|
||||
>
|
||||
{$_("valid-email-is-required")}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<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
|
||||
autocomplete="off"
|
||||
placeholder={$_('phone')}
|
||||
placeholder={$_("phone")}
|
||||
type="tel"
|
||||
bind:value={editable.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 class="text-sm w-full">
|
||||
<span class="font-medium text-gray-700">{$_('group')}</span>
|
||||
<span class="font-medium text-gray-700">{$_("group")}</span>
|
||||
<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"
|
||||
itemFilter={(label, filterText, option) => label
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
filterText.toLowerCase()
|
||||
) || option.id.value
|
||||
.toString()
|
||||
.startsWith(filterText.toLowerCase())}
|
||||
itemFilter={(label, filterText, option) =>
|
||||
label.toLowerCase().includes(filterText.toLowerCase()) ||
|
||||
option.id.value.toString().startsWith(filterText.toLowerCase())}
|
||||
items={groups}
|
||||
showChevron={true}
|
||||
placeholder={$_('search-for-an-organization-or-team-by-name-or-id')}
|
||||
noOptionsMessage={$_('no-organization-or-team-found')}
|
||||
placeholder={$_("search-for-an-organization-or-team-by-name-or-id")}
|
||||
noOptionsMessage={$_("no-organization-or-team-found")}
|
||||
bind:selectedValue={group}
|
||||
on:select={(selectedValue) => {
|
||||
editable.group = selectedValue.detail.value.id;
|
||||
}}
|
||||
on:clear={() => (editable.group = null)} />
|
||||
on:clear={() => (editable.group = null)}
|
||||
/>
|
||||
</div>
|
||||
<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 />
|
||||
<span class="text-gray-700">{original_data.distance /1000 } km</span>
|
||||
<span class="text-gray-700">{original_data.distance / 1000} km</span>
|
||||
</div>
|
||||
</section>
|
||||
{:catch error}
|
||||
|
@@ -7,35 +7,43 @@
|
||||
$: current_runners = [];
|
||||
export let modal_open = false;
|
||||
export let import_modal_open = false;
|
||||
let addRunners;
|
||||
</script>
|
||||
|
||||
<section class="container p-5">
|
||||
<span class="mb-1 text-3xl font-extrabold leading-tight">
|
||||
{$_('runners')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:CREATE')}
|
||||
{$_("runners")}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER: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">
|
||||
{$_('laeufer-hinzufuegen')}
|
||||
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"
|
||||
>
|
||||
{$_("laeufer-hinzufuegen")}
|
||||
</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
import_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">
|
||||
{$_('import-runners')}
|
||||
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"
|
||||
>
|
||||
{$_("import-runners")}
|
||||
</button>
|
||||
{/if}
|
||||
</span>
|
||||
<RunnersOverview bind:current_runners />
|
||||
<RunnersOverview bind:current_runners bind:addRunners />
|
||||
</section>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:CREATE')}
|
||||
<AddRunnerModal bind:current_runners bind:modal_open />
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:CREATE")}
|
||||
<AddRunnerModal
|
||||
bind:modal_open
|
||||
on:created={(event) => {
|
||||
addRunners(event.detail.runners);
|
||||
}}
|
||||
/>
|
||||
<ImportRunnerModal
|
||||
on:cancelDelete={(event) => {
|
||||
import_modal_open = false;
|
||||
@@ -43,7 +51,10 @@
|
||||
passed_team={{}}
|
||||
passed_orgs={[]}
|
||||
passed_org={{}}
|
||||
bind:current_runners
|
||||
opened_from="RunnerOverview"
|
||||
bind:import_modal_open />
|
||||
bind:import_modal_open
|
||||
on:created={(event) => {
|
||||
addRunners(event.detail.runners);
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
@@ -1,267 +1,267 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import {
|
||||
RunnerOrganizationService,
|
||||
RunnerService,
|
||||
RunnerTeamService,
|
||||
RunnerOrganizationService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import {
|
||||
createSvelteTable,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
renderComponent,
|
||||
} from "@tanstack/svelte-table";
|
||||
import { onMount } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { writable } from "svelte/store";
|
||||
import store from "../../store";
|
||||
import RunnersEmptyState from "./RunnersEmptyState.svelte";
|
||||
import Select from "svelte-select";
|
||||
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
|
||||
import GenerateRunnerCards from "../pdf_generation/GenerateRunnerCards.svelte";
|
||||
import GenerateRunnerCertificates from "../pdf_generation/GenerateRunnerCertificates.svelte";
|
||||
let pageLoaded = false;
|
||||
$: searchvalue = "";
|
||||
$: active_deletes = [];
|
||||
import GenerateSponsoringContracts from "../pdf_generation/GenerateSponsoringContracts.svelte";
|
||||
import InputElement from "../shared/InputElement.svelte";
|
||||
import TableActions from "../shared/TableActions.svelte";
|
||||
import { groupFilter } from "../shared/tablefilters";
|
||||
import DeleteRunnerModal from "./DeleteRunnerModal.svelte";
|
||||
import Toastify from "toastify-js";
|
||||
import TableBottom from "../shared/TableBottom.svelte";
|
||||
import TableHeader from "../shared/TableHeader.svelte";
|
||||
|
||||
$: selectedRunners =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.original) || [];
|
||||
$: selected =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.index) || [];
|
||||
|
||||
$: active_delete = undefined;
|
||||
let dataLoaded = false;
|
||||
export let current_runners = [];
|
||||
const runners_promise = RunnerService.runnerControllerGetAll().then((val) => {
|
||||
current_runners = val;
|
||||
pageLoaded = true;
|
||||
});
|
||||
$: selectedFilter_teams = null;
|
||||
$: selectedFilter = null;
|
||||
$: filter__teams = selectedFilter_teams || [];
|
||||
$: 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);
|
||||
$: sponsoring_contracts_show = selected.length > 0;
|
||||
$: cards_show = selected.length > 0;
|
||||
$: certificates_show = selected.length > 0;
|
||||
$: teams = [];
|
||||
$: orgs = [];
|
||||
$: mappedteams = teams.map(function (g) {
|
||||
return { value: g.id, label: g.parentGroup.name + " > " + g.name };
|
||||
});
|
||||
$: selectgroups = orgs
|
||||
.map(function (g) {
|
||||
return { value: g.id, label: g.name };
|
||||
})
|
||||
.concat(mappedteams);
|
||||
|
||||
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
|
||||
teams = val;
|
||||
export const addRunners = (runners) => {
|
||||
current_runners = current_runners.concat(...runners);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_runners,
|
||||
}));
|
||||
};
|
||||
|
||||
//Section table
|
||||
const columns = [
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: () => "id",
|
||||
filterFn: `equalsString`,
|
||||
},
|
||||
{
|
||||
accessorKey: "firstname",
|
||||
header: () => $_("first-name"),
|
||||
filterFn: `includesString`,
|
||||
},
|
||||
{
|
||||
accessorKey: "middlename",
|
||||
header: () => $_("middle-name"),
|
||||
cell: (info) => {
|
||||
if (!info || !info.getValue()) {
|
||||
return "";
|
||||
}
|
||||
return info.getValue();
|
||||
},
|
||||
filterFn: `includesString`,
|
||||
},
|
||||
{
|
||||
accessorKey: "lastname",
|
||||
header: () => $_("last-name"),
|
||||
filterFn: `includesString`,
|
||||
},
|
||||
{
|
||||
accessorKey: "group",
|
||||
header: () => $_("group"),
|
||||
cell: (info) => {
|
||||
const group = info.getValue();
|
||||
if (group.responseType === "RUNNERORGANIZATION") {
|
||||
return group.name;
|
||||
}
|
||||
return `${group.parentGroup.name} > ${group.name}`;
|
||||
},
|
||||
filterFn: `group`,
|
||||
},
|
||||
{
|
||||
accessorKey: "distance",
|
||||
header: () => $_("distance"),
|
||||
cell: (info) => {
|
||||
if (info.getValue() < 1000) {
|
||||
return `${info.getValue()} m`;
|
||||
}
|
||||
return `${(info.getValue() / 1000).toFixed(1)} km`;
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "actions",
|
||||
header: () => $_("action"),
|
||||
cell: (info) => {
|
||||
return renderComponent(TableActions, {
|
||||
detailsLink: `./${info.row.original.id}`,
|
||||
deleteAction: () => {
|
||||
active_delete =
|
||||
current_runners[
|
||||
current_runners.findIndex((r) => r.id == info.row.original.id)
|
||||
];
|
||||
},
|
||||
deleteEnabled:
|
||||
store.state.jwtinfo.userdetails.permissions.includes(
|
||||
"RUNNER:DELETE"
|
||||
),
|
||||
});
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
},
|
||||
];
|
||||
const options = writable({
|
||||
data: [],
|
||||
columns: columns,
|
||||
filterFns: {
|
||||
group: groupFilter,
|
||||
},
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: 50,
|
||||
},
|
||||
},
|
||||
enableRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
});
|
||||
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;
|
||||
const table = createSvelteTable(options);
|
||||
|
||||
async function deleteRunner(delete_runner_id) {
|
||||
await RunnerService.runnerControllerRemove(delete_runner_id, true);
|
||||
current_runners = current_runners.filter((r) => r.id !== delete_runner_id);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_runners,
|
||||
}));
|
||||
Toastify({
|
||||
text: $_("runner-deleted"),
|
||||
duration: 3500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
|
||||
teams = val;
|
||||
});
|
||||
RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
|
||||
(val) => {
|
||||
orgs = val;
|
||||
}
|
||||
);
|
||||
|
||||
let page = 0;
|
||||
while (page >= 0) {
|
||||
const runners = await RunnerService.runnerControllerGetAll(page, 1000);
|
||||
if (runners.length == 0) {
|
||||
page = -2;
|
||||
}
|
||||
|
||||
current_runners = current_runners.concat(...runners);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_runners,
|
||||
}));
|
||||
|
||||
dataLoaded = true;
|
||||
page++;
|
||||
}
|
||||
console.log("All runners loaded");
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')}
|
||||
{#await runners_promise}
|
||||
<DeleteRunnerModal
|
||||
delete_runner={active_delete}
|
||||
modal_open={active_delete != undefined}
|
||||
on:delete={(event) => {
|
||||
deleteRunner(event.detail.id);
|
||||
}}
|
||||
/>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("RUNNER:GET")}
|
||||
{#if !dataLoaded}
|
||||
<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">{$_('runners-are-being-loaded')}</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
role="alert"
|
||||
>
|
||||
<p class="font-bold">{$_("runners-are-being-loaded")}</p>
|
||||
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_runners.length === 0}
|
||||
<RunnersEmptyState />
|
||||
{:else}
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
<div class="block mb-6">
|
||||
<label
|
||||
for="country"
|
||||
class="text-sm font-medium text-gray-700">{$_('filter-by-organization-team')}</label>
|
||||
<Select
|
||||
on:select={(event) => {
|
||||
selectedFilter = event.detail;
|
||||
}}
|
||||
selectedValue={selectedFilter}
|
||||
placeholder={$_('filter-by-organization-team')}
|
||||
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"
|
||||
items={selectgroups}
|
||||
isMulti={true} />
|
||||
</div>
|
||||
<div class="h-12">
|
||||
<GenerateSponsoringContracts
|
||||
bind:sponsoring_contracts_show
|
||||
bind:generate_runners />
|
||||
<GenerateRunnerCards
|
||||
bind:cards_show
|
||||
bind:generate_runners />
|
||||
<GenerateRunnerCertificates
|
||||
bind:certificates_show
|
||||
bind:generate_runners />
|
||||
</div>
|
||||
<div
|
||||
class:hidden={!pageLoaded}
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll"
|
||||
>
|
||||
<table class="divide-y divide-gray-200 w-full table-fixed">
|
||||
<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">
|
||||
<span
|
||||
on:click={() => {
|
||||
const newstate = !current_runners.some((r) => r.is_selected === true);
|
||||
current_runners = current_runners.map((r) => {
|
||||
r.is_selected = newstate;
|
||||
return r;
|
||||
});
|
||||
}}
|
||||
class="underline cursor-pointer select-none">{#if current_runners.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">
|
||||
{$_('name')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('contact-information')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('group')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('distance-in-km')}
|
||||
</th>
|
||||
<th scope="col" class="relative px-6 py-3">
|
||||
<span class="sr-only">{$_('action')}</span>
|
||||
{:else}
|
||||
<div class="h-12 mt-2">
|
||||
<GenerateSponsoringContracts
|
||||
bind:sponsoring_contracts_show
|
||||
bind:generate_runners={selectedRunners}
|
||||
/>
|
||||
<GenerateRunnerCards
|
||||
bind:cards_show
|
||||
bind:generate_runners={selectedRunners}
|
||||
/>
|
||||
<GenerateRunnerCertificates
|
||||
bind:certificates_show
|
||||
bind:generate_runners={selectedRunners}
|
||||
/>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
{#each $table.getHeaderGroups() as headerGroup}
|
||||
<tr class="select-none">
|
||||
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={$table.getIsAllRowsSelected()}
|
||||
indeterminate={$table.getIsSomeRowsSelected()}
|
||||
on:change={() => $table.toggleAllRowsSelected()}
|
||||
/>
|
||||
</th>
|
||||
{#each headerGroup.headers as header}
|
||||
<TableHeader {header} />
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
{#each current_runners as runner}
|
||||
{#if runner.firstname
|
||||
.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.parentGroup.name} > {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 /1000 } km
|
||||
</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>
|
||||
{/each}
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each $table.getRowModel().rows as row}
|
||||
<tr>
|
||||
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={row.getIsSelected()}
|
||||
on:change={() => row.toggleSelected()}
|
||||
/>
|
||||
</td>
|
||||
{#each row.getVisibleCells() as cell}
|
||||
<td>
|
||||
<svelte:component
|
||||
this={flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
<div class="h-2" />
|
||||
{/if}
|
||||
{/if}
|
||||
<TableBottom {table} {selected} />
|
||||
|
35
src/components/runners/ThFilterGroup.svelte
Normal file
35
src/components/runners/ThFilterGroup.svelte
Normal file
@@ -0,0 +1,35 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let groups;
|
||||
export let handler;
|
||||
let selected = "all";
|
||||
</script>
|
||||
|
||||
<th style="border-bottom: 1px solid #ddd;">
|
||||
<select
|
||||
on:input={() => {
|
||||
setTimeout(() => {
|
||||
if (`${selected}`.trim()) {
|
||||
const value = selected;
|
||||
handler.filter(value, (runner) => {
|
||||
if (
|
||||
runner.group.id === value ||
|
||||
runner?.group?.parentGroup?.id === value ||
|
||||
value === "all"
|
||||
)
|
||||
return runner;
|
||||
return "";
|
||||
});
|
||||
}
|
||||
}, 50);
|
||||
}}
|
||||
bind:value={selected}
|
||||
name="groupfilter"
|
||||
id="groupfilter"
|
||||
>
|
||||
<option value="all">{$_('all')}</option>
|
||||
{#each groups as g}
|
||||
<option value={g.value}>{g.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</th>
|
@@ -1,23 +1,24 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import {
|
||||
RunnerService,
|
||||
ScanService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import Select from "svelte-select";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let modal_open;
|
||||
export let current_scans;
|
||||
const getRunnerLabel = (option) =>
|
||||
option.firstname + " " + (option.middlename || "") + " " + option.lastname;
|
||||
const filterRunners = (label, filterText, option) =>
|
||||
label.toLowerCase().includes(filterText.toLowerCase()) ||
|
||||
option.value.toString().startsWith(filterText.toLowerCase());
|
||||
option.value.id.toString().startsWith(filterText.toLowerCase());
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
const dispatch = createEventDispatcher();
|
||||
$: runner = 0;
|
||||
$: runners = [];
|
||||
RunnerService.runnerControllerGetAll().then((val) => {
|
||||
@@ -63,8 +64,7 @@
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_scans.push(result);
|
||||
current_scans = current_scans;
|
||||
dispatch("created", { scans: [result] });
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -81,7 +81,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
110
src/components/scans/DeleteScanModal.svelte
Normal file
110
src/components/scans/DeleteScanModal.svelte
Normal file
@@ -0,0 +1,110 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { createEventDispatcher, onMount } from "svelte";
|
||||
export let modal_open;
|
||||
export let delete_scan = {
|
||||
id: 0,
|
||||
runner: {
|
||||
firstname: "",
|
||||
lastname: "",
|
||||
},
|
||||
};
|
||||
const dispatch = createEventDispatcher();
|
||||
onMount(() => {
|
||||
document.onkeydown = (e) => {
|
||||
e = e || window.event;
|
||||
if (e.key === "Escape") {
|
||||
modal_open = false;
|
||||
}
|
||||
if (e.keyCode === 13) {
|
||||
if (createbtnenabled === true) {
|
||||
createbtnenabled = false;
|
||||
submit();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
async function submit() {
|
||||
dispatch("delete", { id: delete_scan.id });
|
||||
modal_open = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
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">​</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
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
class="h-6 w-6 text-blue-600"
|
||||
fill="currentColor"
|
||||
width="24"
|
||||
height="24"
|
||||
><path fill="none" d="M0 0h24v24H0z" />
|
||||
<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"
|
||||
/></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">
|
||||
{$_("confirm-delete")}
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
{$_("please-confirm-the-deletion-of-scan")} #{delete_scan.id}
|
||||
</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={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"
|
||||
>
|
||||
{$_("delete")}
|
||||
</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}
|
16
src/components/scans/ScanValid.svelte
Normal file
16
src/components/scans/ScanValid.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let valid = false;
|
||||
</script>
|
||||
|
||||
{#if valid}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
|
||||
>{$_("valid")}</span
|
||||
>
|
||||
{:else}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800"
|
||||
>{$_("invalid")}</span
|
||||
>
|
||||
{/if}
|
@@ -5,6 +5,7 @@
|
||||
import ScansOverview from "./ScansOverview.svelte";
|
||||
$: current_scans = [];
|
||||
export let modal_open = false;
|
||||
let addScans;
|
||||
</script>
|
||||
|
||||
<section class="container p-5">
|
||||
@@ -21,9 +22,11 @@
|
||||
</button>
|
||||
{/if}
|
||||
</span>
|
||||
<ScansOverview bind:current_scans />
|
||||
<ScansOverview bind:current_scans bind:addScans />
|
||||
</section>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('SCAN:CREATE')}
|
||||
<AddScanModal bind:current_scans bind:modal_open />
|
||||
<AddScanModal bind:modal_open on:created={(event)=>{
|
||||
addScans(event.detail.scans)
|
||||
}} />
|
||||
{/if}
|
||||
|
@@ -1,197 +1,308 @@
|
||||
<script>
|
||||
import { getLocaleFromNavigator, _ } from "svelte-i18n";
|
||||
import {
|
||||
ScanService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { ScanService, TrackService } from "@odit/lfk-client-js";
|
||||
import store from "../../store";
|
||||
import {
|
||||
createSvelteTable,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
renderComponent,
|
||||
} from "@tanstack/svelte-table";
|
||||
import { onMount } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
import Toastify from "toastify-js";
|
||||
import TableBottom from "../shared/TableBottom.svelte";
|
||||
import TableHeader from "../shared/TableHeader.svelte";
|
||||
import ScansEmptyState from "./ScansEmptyState.svelte";
|
||||
$: searchvalue = "";
|
||||
$: active_deletes = [];
|
||||
import InputElement from "../shared/InputElement.svelte";
|
||||
import TableActions from "../shared/TableActions.svelte";
|
||||
import { runnerFilter, statusFilter } from "../shared/tablefilters";
|
||||
import CardRunner from "../cards/CardRunner.svelte";
|
||||
import ScanValid from "./ScanValid.svelte";
|
||||
import DeleteScanModal from "./DeleteScanModal.svelte";
|
||||
|
||||
$: selectedScans =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.original) || [];
|
||||
$: selected =
|
||||
$table?.getSelectedRowModel().rows.map((row) => row.index) || [];
|
||||
|
||||
$: active_delete = undefined;
|
||||
$: dataLoaded = false;
|
||||
export let current_scans = [];
|
||||
const scans_promise = ScanService.scanControllerGetAll().then((val) => {
|
||||
current_scans = val;
|
||||
export const addScans = (scans) => {
|
||||
current_scans = current_scans.concat(...scans);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_scans,
|
||||
}));
|
||||
};
|
||||
|
||||
let allTracks = [];
|
||||
TrackService.trackControllerGetAll().then((val) => {
|
||||
allTracks = val;
|
||||
});
|
||||
function should_display_based_on_id(id) {
|
||||
if (searchvalue.toString().slice(-1) === "*") {
|
||||
return id.toString().startsWith(searchvalue.replace("*", ""));
|
||||
function format_laptime(laptime) {
|
||||
if (laptime == 0 || laptime == null) {
|
||||
return $_("first-scan-of-the-day");
|
||||
}
|
||||
return id.toString() === searchvalue;
|
||||
if (laptime < 60) {
|
||||
return `${laptime}s`;
|
||||
}
|
||||
if (laptime < 3600) {
|
||||
return `${Math.floor(laptime / 60)}min ${
|
||||
laptime - Math.floor(laptime / 60) * 60
|
||||
}s`;
|
||||
}
|
||||
return `${Math.floor(laptime / 3600)}h ${
|
||||
laptime - Math.floor(laptime / 3600) * 3600
|
||||
}min ${
|
||||
laptime -
|
||||
Math.floor(laptime / 3600) * 3600 -
|
||||
Math.floor(laptime / 60) * 60
|
||||
}`;
|
||||
}
|
||||
function format_laptime(laptime){
|
||||
if(laptime == 0 || laptime == null){return $_('first-scan-of-the-day')}
|
||||
if(laptime < 60){return `${laptime}s`}
|
||||
if(laptime < 3600){return `${Math.floor(laptime / 60)}min ${laptime - (Math.floor(laptime / 60)*60)}s`}
|
||||
return `${Math.floor(laptime / 3600)}h ${laptime - (Math.floor(laptime / 3600)*3600)}min ${laptime - (Math.floor(laptime / 3600)*3600) - (Math.floor(laptime / 60)*60)}`
|
||||
|
||||
const columns = [
|
||||
{
|
||||
accessorKey: "id",
|
||||
header: () => "id",
|
||||
filterFn: `equalsString`,
|
||||
},
|
||||
{
|
||||
accessorKey: "runner",
|
||||
header: () => $_("runner"),
|
||||
cell: (info) => {
|
||||
return renderComponent(CardRunner, { runner: info.getValue() });
|
||||
},
|
||||
filterFn: `runner`,
|
||||
},
|
||||
{
|
||||
accessorKey: "lapTime",
|
||||
header: () => $_("laptime"),
|
||||
cell: (info) => {
|
||||
return format_laptime(info.getValue());
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "timestamp",
|
||||
header: () => $_("timestamp"),
|
||||
cell: (info) => {
|
||||
return new Date(parseInt(info.getValue()) * 1000).toLocaleString();
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "distance",
|
||||
header: () => $_("distance"),
|
||||
cell: (info) => {
|
||||
if (info.getValue() < 1000) {
|
||||
return `${info.getValue()}m`;
|
||||
}
|
||||
return `${(info.getValue() / 1000).toFixed(1)}km`;
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "track",
|
||||
header: () => $_("track"),
|
||||
cell: (info) => {
|
||||
const track = info.getValue();
|
||||
return track?.name || "?";
|
||||
},
|
||||
enableColumnFilter: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "valid",
|
||||
cell: (info) => {
|
||||
return renderComponent(ScanValid, { valid: info.getValue() });
|
||||
},
|
||||
header: () => $_("status"),
|
||||
filterFn: `status`,
|
||||
},
|
||||
{
|
||||
accessorKey: "actions",
|
||||
header: () => $_("action"),
|
||||
cell: (info) => {
|
||||
return renderComponent(TableActions, {
|
||||
detailsLink: `./${info.row.original.id}`,
|
||||
deleteAction: () => {
|
||||
active_delete =
|
||||
current_scans[
|
||||
current_scans.findIndex((r) => r.id == info.row.original.id)
|
||||
];
|
||||
},
|
||||
deleteEnabled:
|
||||
store.state.jwtinfo.userdetails.permissions.includes("SCAN:DELETE"),
|
||||
});
|
||||
},
|
||||
enableColumnFilter: false,
|
||||
enableSorting: false,
|
||||
},
|
||||
];
|
||||
const options = writable({
|
||||
data: [],
|
||||
columns: columns,
|
||||
initialState: {
|
||||
pagination: {
|
||||
pageSize: 50,
|
||||
},
|
||||
},
|
||||
filterFns: {
|
||||
runner: runnerFilter,
|
||||
status: statusFilter,
|
||||
},
|
||||
enableRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
});
|
||||
const table = createSvelteTable(options);
|
||||
async function deleteScan(scan_id) {
|
||||
await ScanService.scanControllerRemove(scan_id, true);
|
||||
current_scans = current_scans.filter((r) => r.id !== scan_id);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_scans,
|
||||
}));
|
||||
Toastify({
|
||||
text: $_("scan-deleted"),
|
||||
duration: 3500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
let page = 0;
|
||||
while (page >= 0) {
|
||||
const scans = await ScanService.scanControllerGetAll(page, 500);
|
||||
if (scans.length == 0) {
|
||||
page = -2;
|
||||
}
|
||||
|
||||
current_scans = current_scans.concat(...scans);
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_scans,
|
||||
}));
|
||||
|
||||
dataLoaded = true;
|
||||
page++;
|
||||
}
|
||||
console.log("All scans loaded");
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('SCAN:GET')}
|
||||
{#await scans_promise}
|
||||
<DeleteScanModal
|
||||
delete_scan={active_delete}
|
||||
modal_open={active_delete != undefined}
|
||||
on:delete={(event) => {
|
||||
deleteScan(event.detail.id);
|
||||
}}
|
||||
/>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes("SCAN:GET")}
|
||||
{#if !dataLoaded}
|
||||
<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">{$_('scans-are-being-loaded')}</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
role="alert"
|
||||
>
|
||||
<p class="font-bold">{$_("scans-are-being-loaded")}</p>
|
||||
<p class="text-sm">{$_("this-might-take-a-moment")}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_scans.length === 0}
|
||||
<ScansEmptyState />
|
||||
{: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">
|
||||
{$_('runner')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('distance-track')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('laptime')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('status')}
|
||||
</th>
|
||||
<th scope="col" class="relative px-6 py-3">
|
||||
<span class="sr-only">{$_('action')}</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200">
|
||||
{#each current_scans as scan}
|
||||
{#if scan.track?.name
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || scan.runner?.firstname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || scan.runner?.lastname
|
||||
.toLowerCase()
|
||||
.includes(
|
||||
searchvalue.toLowerCase()
|
||||
) || should_display_based_on_id(scan.id)}
|
||||
<tr data-rowid="scan_{scan.id}">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<a
|
||||
href="../runners/{scan.runner.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{scan.runner.firstname}
|
||||
{scan.runner.middlename || ''}
|
||||
{scan.runner.lastname}</a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{#if scan.distance < 1000}
|
||||
{scan.distance}m
|
||||
{:else}{scan.distance / 1000}km{/if}
|
||||
{#if scan.track}
|
||||
<a
|
||||
href="../tracks"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{scan.track.name}
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if scan.responseType === "TRACKSCAN"}
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{format_laptime(scan.lapTime)}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{$_('scan-with-fixed-distance')}
|
||||
</div>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
{#if scan.valid}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">{$_('valid')}</span>
|
||||
{:else}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">{$_('invalid')}</span>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
{#if active_deletes[scan.id] === true}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[scan.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">{$_('cancel-delete')}</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
ScanService.scanControllerRemove(scan.id, false).then(
|
||||
(resp) => {
|
||||
current_scans = current_scans.filter(
|
||||
(obj) => obj.id !== scan.id
|
||||
);
|
||||
Toastify({
|
||||
text: 'Scan deleted',
|
||||
duration: 500,
|
||||
backgroundColor:
|
||||
'linear-gradient(to right, #00b09b, #96c93d)',
|
||||
}).showToast();
|
||||
}
|
||||
);
|
||||
}}
|
||||
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="./{scan.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900">{$_('details')}</a>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('SCAN:DELETE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[scan.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>
|
||||
{:else if current_scans.length === 0}
|
||||
<ScansEmptyState />
|
||||
{:else}
|
||||
{#if selected.length > 0}
|
||||
<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 inline-flex"
|
||||
id="options-menu"
|
||||
on:click={async () => {
|
||||
const prom = [];
|
||||
for (const scan of selectedScans) {
|
||||
prom.push(ScanService.scanControllerRemove(scan.id, true));
|
||||
}
|
||||
await Promise.all(prom);
|
||||
for (const scan of selectedScans) {
|
||||
current_scans = current_scans.filter((r) => r.id !== scan.id);
|
||||
}
|
||||
options.update((options) => ({
|
||||
...options,
|
||||
data: current_scans,
|
||||
}));
|
||||
$table.resetRowSelection();
|
||||
Toastify({
|
||||
text: $_("scan-deleted"),
|
||||
duration: 3500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
}}
|
||||
>
|
||||
{$_("delete-scans")}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="w-5 h-5"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
{/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 class="overflow-x-auto">
|
||||
<table class="w-full">
|
||||
<thead>
|
||||
{#each $table.getHeaderGroups() as headerGroup}
|
||||
<tr class="select-none">
|
||||
<th class="inset-y-0 left-0 px-4 py-2 text-left w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={$table.getIsAllRowsSelected()}
|
||||
indeterminate={$table.getIsSomeRowsSelected()}
|
||||
on:change={() => $table.toggleAllRowsSelected()}
|
||||
/>
|
||||
</th>
|
||||
{#each headerGroup.headers as header}
|
||||
<TableHeader {header} />
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each $table.getRowModel().rows as row}
|
||||
<tr>
|
||||
<td class="inset-y-0 left-0 px-4 py-2 text-center w-px">
|
||||
<InputElement
|
||||
type="checkbox"
|
||||
checked={row.getIsSelected()}
|
||||
on:change={() => row.toggleSelected()}
|
||||
/>
|
||||
</td>
|
||||
{#each row.getVisibleCells() as cell}
|
||||
<td>
|
||||
<svelte:component
|
||||
this={flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
/>
|
||||
</td>
|
||||
{/each}
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/await}
|
||||
<TableBottom {table} {selected} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
50
src/components/scans/ThFilterRunner.svelte
Normal file
50
src/components/scans/ThFilterRunner.svelte
Normal file
@@ -0,0 +1,50 @@
|
||||
<script>
|
||||
export let handler;
|
||||
let filterValue = "";
|
||||
</script>
|
||||
|
||||
<th>
|
||||
<input
|
||||
on:input={() => {
|
||||
setTimeout(() => {
|
||||
const v = filterValue.toLowerCase();
|
||||
handler.filter(v, (c) => {
|
||||
if (v.startsWith("#")) {
|
||||
return `#${c.runner?.id}`;
|
||||
}
|
||||
if (c.runner) {
|
||||
let runnerName = `${c.runner.firstname} ${c.runner.lastname}`;
|
||||
if (c.runner.middlename) {
|
||||
runnerName = `${c.runner.firstname} ${c.runner.middlename} ${c.runner.lastname}`;
|
||||
}
|
||||
runnerName = runnerName.toLowerCase();
|
||||
return runnerName;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
}, 150);
|
||||
}}
|
||||
placeholder="Filter"
|
||||
bind:value={filterValue}
|
||||
type="text"
|
||||
name="runnerfilter"
|
||||
id="runnerfilter"
|
||||
/>
|
||||
</th>
|
||||
|
||||
<style>
|
||||
th {
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
input {
|
||||
margin: -1px 0 0 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
border: none;
|
||||
text-align: left;
|
||||
background: inherit;
|
||||
outline: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
31
src/components/scans/ThFilterTrack.svelte
Normal file
31
src/components/scans/ThFilterTrack.svelte
Normal file
@@ -0,0 +1,31 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
export let tracks;
|
||||
export let handler;
|
||||
let selected = "all";
|
||||
</script>
|
||||
|
||||
<th style="border-bottom: 1px solid #ddd;">
|
||||
<select
|
||||
on:input={() => {
|
||||
setTimeout(() => {
|
||||
if (`${selected}`.trim()) {
|
||||
const value = selected;
|
||||
handler.filter(value, (scan) => {
|
||||
// TODO: fix filter
|
||||
if (scan.track.id === value || value === "all") return scan.track.id;
|
||||
return "";
|
||||
});
|
||||
}
|
||||
}, 50);
|
||||
}}
|
||||
bind:value={selected}
|
||||
name="trackfilter"
|
||||
id="trackfilter"
|
||||
>
|
||||
<option value="all">{$_("all")}</option>
|
||||
{#each tracks as track}
|
||||
<option value={track.id}>{track.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</th>
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { ScanStationService, TrackService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import Select from "svelte-select";
|
||||
@@ -81,7 +81,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<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";
|
||||
@@ -13,7 +13,7 @@
|
||||
dispatch("cancelDelete", { id: delete_station.id });
|
||||
}
|
||||
function deleteStation() {
|
||||
ScanStationService.donorControllerRemove(
|
||||
ScanStationService.scanStationControllerRemove(
|
||||
delete_station.id,
|
||||
true
|
||||
)
|
||||
@@ -32,7 +32,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={cancelDelete}>
|
||||
<div
|
||||
|
@@ -1,14 +1,18 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
import Toastify from "toastify-js";
|
||||
import { tick, createEventDispatcher } from "svelte";
|
||||
import bwipjs from "bwip-js";
|
||||
|
||||
export let copy_modal_open;
|
||||
export let new_station;
|
||||
const dispatch = createEventDispatcher();
|
||||
let valueCopy = null;
|
||||
let areaDom;
|
||||
let copied = false;
|
||||
$: is_qrcode = false;
|
||||
$: barcode = textToBase64Barcode(new_station.key, is_qrcode);
|
||||
|
||||
function close() {
|
||||
copy_modal_open = false;
|
||||
}
|
||||
@@ -36,91 +40,167 @@
|
||||
"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;
|
||||
}
|
||||
|
||||
function textToBase64Barcode(text, is_qrcode) {
|
||||
const canvas = document.createElement("canvas");
|
||||
let bcid = "code128";
|
||||
if (is_qrcode) {
|
||||
bcid = "qrcode";
|
||||
}
|
||||
let codeconfig = {
|
||||
bcid,
|
||||
text: `${text}`,
|
||||
scale: 4,
|
||||
includetext: true,
|
||||
textxalign: "center",
|
||||
backgroundcolor: "ffffff",
|
||||
};
|
||||
if (bcid == "code128") {
|
||||
codeconfig.height = 10;
|
||||
}
|
||||
bwipjs.toCanvas(canvas, codeconfig);
|
||||
return canvas.toDataURL("image/png");
|
||||
}
|
||||
</script>
|
||||
|
||||
{#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="fixed z-10 inset-0 overflow-y-auto">
|
||||
<div
|
||||
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
|
||||
>
|
||||
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
||||
<div
|
||||
class="absolute inset-0 bg-gray-500 opacity-75"
|
||||
data-id="modal_backdrop" />
|
||||
data-id="modal_backdrop"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
aria-hidden="true">​</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">
|
||||
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">
|
||||
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" />
|
||||
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>
|
||||
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')}
|
||||
{$_("token")}
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
{$_('the-scanstations-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again')}
|
||||
{$_(
|
||||
"the-scanstations-api-token-will-only-get-displayed-once-you-wont-be-able-to-change-or-view-it-again"
|
||||
)}
|
||||
<br />
|
||||
{$_('please-copy-the-token-and-store-it-somewhere-save')}
|
||||
{$_("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">
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("token")}</label
|
||||
>
|
||||
<button 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">
|
||||
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 p-2"
|
||||
>
|
||||
{new_station.key}
|
||||
</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">
|
||||
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" />
|
||||
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>
|
||||
d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z"
|
||||
/></svg
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<p class="text-gray-500 text-xs">
|
||||
{$_('click-to-copy-token-to-clipboard')}
|
||||
{$_("click-to-copy-token-to-clipboard")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx-auto text-center items-center">
|
||||
<h2 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{$_("config-codes")}
|
||||
</h2>
|
||||
<span class="flex items-center text-center">
|
||||
<p class="text-md text-gray-900 mr-3">Format:</p>
|
||||
<label for="codeswitch" class="text-md text-gray-900 mr-3"
|
||||
>Code128</label
|
||||
>
|
||||
<input
|
||||
id="codeswitch"
|
||||
type="checkbox"
|
||||
bind:checked={is_qrcode}
|
||||
class="relative shrink-0 w-[3.25rem] h-7 bg-gray-100 checked:bg-none checked:bg-blue-600 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 border border-transparent ring-1 ring-transparent focus:border-blue-600 focus:ring-blue-600 ring-offset-white focus:outline-none appearance-none before:inline-block before:w-6 before:h-6 before:bg-white checked:before:bg-blue-200 before:translate-x-0 checked:before:translate-x-full before:shadow before:rounded-full before:transform before:ring-0 before:transition before:ease-in-out before:duration-200 dark:before:bg-gray-400 dark:checked:before:bg-blue-200"
|
||||
/>
|
||||
<label for="codeswitch" class="text-md text-gray-900 ml-3"
|
||||
>QR-Code</label
|
||||
>
|
||||
</span>
|
||||
<h3 class="leading-6 font-medium text-gray-900">
|
||||
{$_("api-endpoint")}
|
||||
</h3>
|
||||
<img
|
||||
class:w-[50%]={is_qrcode}
|
||||
class:w-full={!is_qrcode}
|
||||
class="md:w-auto mb-2 mx-auto"
|
||||
alt="Registrierungscode"
|
||||
src={textToBase64Barcode(config.baseurl, is_qrcode)}
|
||||
/>
|
||||
<h3 class="leading-6 font-medium text-gray-900">{$_("token")}</h3>
|
||||
<img
|
||||
class:w-[50%]={is_qrcode}
|
||||
class:w-full={!is_qrcode}
|
||||
class="md:w-auto mb-2 mx-auto"
|
||||
alt="Registrierungscode"
|
||||
src={barcode}
|
||||
/>
|
||||
</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')}
|
||||
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>
|
||||
|
@@ -41,7 +41,7 @@
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
class="mb-4" />
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||
<table class="divide-y divide-gray-200 w-full">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { MeService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
@@ -30,7 +30,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={cancelDelete}>
|
||||
<div
|
||||
|
20
src/components/shared/InputElement.svelte
Normal file
20
src/components/shared/InputElement.svelte
Normal file
@@ -0,0 +1,20 @@
|
||||
<script>
|
||||
let className = "";
|
||||
export { className as class };
|
||||
|
||||
export let type;
|
||||
</script>
|
||||
|
||||
<input
|
||||
class={`border-1 border-stone-300 border rounded-md shadow ${className} ${
|
||||
type === "checkbox" && "w-5 h-5 text-orange-400"
|
||||
}`}
|
||||
{type}
|
||||
{...$$restProps}
|
||||
on:click
|
||||
on:change
|
||||
on:keydown
|
||||
on:keyup
|
||||
on:mouseenter
|
||||
on:mouseleave
|
||||
/>
|
26
src/components/shared/TableActions.svelte
Normal file
26
src/components/shared/TableActions.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
|
||||
export let detailsLink;
|
||||
export let detailsAction;
|
||||
export let deleteEnabled;
|
||||
export let deleteAction;
|
||||
</script>
|
||||
|
||||
{#if detailsLink}
|
||||
<a href={detailsLink} class="text-indigo-600 hover:text-indigo-900"
|
||||
>{$_("details")}</a
|
||||
>
|
||||
{:else if detailsAction}
|
||||
<button on:click={detailsAction} class="text-indigo-600 hover:text-indigo-900"
|
||||
>{$_("details")}</button
|
||||
>
|
||||
{/if}
|
||||
{#if deleteEnabled}
|
||||
<button
|
||||
tabindex="0"
|
||||
on:click={deleteAction}
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer"
|
||||
>{$_("delete")}</button
|
||||
>
|
||||
{/if}
|
71
src/components/shared/TableBottom.svelte
Normal file
71
src/components/shared/TableBottom.svelte
Normal file
@@ -0,0 +1,71 @@
|
||||
<script>
|
||||
export let table;
|
||||
export let selected;
|
||||
</script>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="border rounded p-1"
|
||||
on:click={() => $table.setPageIndex(0)}
|
||||
disabled={!$table.getCanPreviousPage()}
|
||||
>
|
||||
{"<<"}
|
||||
</button>
|
||||
<button
|
||||
class="border rounded p-1"
|
||||
on:click={() => $table.previousPage()}
|
||||
disabled={!$table.getCanPreviousPage()}
|
||||
>
|
||||
{"<"}
|
||||
</button>
|
||||
<button
|
||||
class="border rounded p-1"
|
||||
on:click={() => $table.nextPage()}
|
||||
disabled={!$table.getCanNextPage()}
|
||||
>
|
||||
{">"}
|
||||
</button>
|
||||
<button
|
||||
class="border rounded p-1"
|
||||
on:click={() => $table.setPageIndex($table.getPageCount() - 1)}
|
||||
disabled={!$table.getCanNextPage()}
|
||||
>
|
||||
{">>"}
|
||||
</button>
|
||||
<span class="flex items-center gap-1">
|
||||
<div>Page</div>
|
||||
<strong>
|
||||
{$table.getState().pagination.pageIndex + 1} of{" "}
|
||||
{$table.getPageCount()}
|
||||
</strong>
|
||||
</span>
|
||||
<span class="flex items-center gap-1">
|
||||
| Go to page:
|
||||
<input
|
||||
type="number"
|
||||
defaultValue={$table.getState().pagination.pageIndex + 1}
|
||||
on:change={(e) => {
|
||||
const page = e.target.value ? Number(e.target.value) - 1 : 0;
|
||||
$table.setPageIndex(page);
|
||||
}}
|
||||
class="border p-1 rounded w-16"
|
||||
/>
|
||||
</span>
|
||||
<select
|
||||
value={$table.getState().pagination.pageSize}
|
||||
on:input={(e) => {
|
||||
const ps = Number(e.target.value);
|
||||
console.log({ ps });
|
||||
$table.setPageSize(Number(e.target.value));
|
||||
}}
|
||||
>
|
||||
{#each [25, 50, 100, 250, 500] as pageSize}
|
||||
<option value={pageSize}>{pageSize}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<!-- <pre>{JSON.stringify($table.getState(), null, 2)}</pre> -->
|
||||
<div>
|
||||
{Object.keys(selected).length} of{" "}
|
||||
{$table.getPreFilteredRowModel().rows.length} Total Rows Selected
|
||||
</div>
|
57
src/components/shared/TableHeader.svelte
Normal file
57
src/components/shared/TableHeader.svelte
Normal file
@@ -0,0 +1,57 @@
|
||||
<script>
|
||||
import { flexRender } from "@tanstack/svelte-table";
|
||||
export let header;
|
||||
</script>
|
||||
|
||||
<th class="cursor-pointer min-w-[5rem]">
|
||||
{#if !header.isPlaceholder}
|
||||
<button
|
||||
class="w-full"
|
||||
tabindex="0"
|
||||
on:click={header.column.getToggleSortingHandler()}
|
||||
>
|
||||
<svelte:component
|
||||
this={flexRender(header.column.columnDef.header, header.getContext())}
|
||||
/>
|
||||
{#if header.column
|
||||
.getIsSorted()
|
||||
.toString() == "asc" && header.column.getCanSort()}
|
||||
🔼
|
||||
{:else if header.column
|
||||
.getIsSorted()
|
||||
.toString() == "desc" && header.column.getCanSort()}
|
||||
🔽
|
||||
{/if}
|
||||
</button>
|
||||
{/if}
|
||||
{#if header.column.getCanFilter()}
|
||||
<div class="relative max-w-xs">
|
||||
<input
|
||||
title="name-search"
|
||||
value={header.column.getFilterValue() || ""}
|
||||
on:keyup={(e) => {
|
||||
header.column.setFilterValue(e.target.value);
|
||||
}}
|
||||
type="text"
|
||||
class="block w-full rounded-md border-gray-200 py-2 pl-8 text-xs focus:border-blue-500 focus:ring-blue-500"
|
||||
placeholder=""
|
||||
/>
|
||||
<div
|
||||
class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-2"
|
||||
>
|
||||
<svg
|
||||
class="h-3.5 w-3.5 text-gray-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={16}
|
||||
height={16}
|
||||
fill="currentColor"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</th>
|
65
src/components/shared/tablefilters.js
Normal file
65
src/components/shared/tablefilters.js
Normal file
@@ -0,0 +1,65 @@
|
||||
export const groupFilter = (row, columnId, value) => {
|
||||
const group = row.getValue(columnId);
|
||||
if (group.responseType === "RUNNERORGANIZATION") {
|
||||
return group.name.toLowerCase().includes(value.toLowerCase());
|
||||
} else if (value.includes(">")) {
|
||||
return (
|
||||
`${group.parentGroup.name} > ${group.name}`.toLowerCase().includes(value.toLowerCase())
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
group.name.toLowerCase().includes(value.toLowerCase()) ||
|
||||
group.parentGroup.name.toLowerCase().includes(value.toLowerCase())
|
||||
);
|
||||
}
|
||||
};
|
||||
export const runnerFilter = (row, columnId, value) => {
|
||||
const runner = row.getValue(columnId);
|
||||
return filterRunner(runner, value)
|
||||
};
|
||||
|
||||
export const donationRunnerFilter = (row, columnId, value) => {
|
||||
const runner = row.getValue(columnId);
|
||||
if (!runner) { return false; }
|
||||
return filterRunner(runner, value)
|
||||
};
|
||||
|
||||
export const donationDonorFilter = (row, columnId, value) => {
|
||||
const runner = row.getValue(columnId);
|
||||
return filterRunner(runner, value)
|
||||
};
|
||||
|
||||
function filterRunner(runner, value) {
|
||||
if (!runner && value == "blanko") { return true }
|
||||
if (!runner) { return false }
|
||||
|
||||
if (value.startsWith("#")) {
|
||||
return runner.id == value.replace("#", "")
|
||||
}
|
||||
|
||||
if (runner.middlename) {
|
||||
return `${runner.firstname} ${runner.middlename} ${runner.lastname}`.toLowerCase().includes(value.toLowerCase())
|
||||
}
|
||||
return `${runner.firstname} ${runner.lastname}`.toLowerCase().includes(value.toLowerCase())
|
||||
}
|
||||
|
||||
export const filterName = (row, columnId, value) => {
|
||||
const obj = row.original;
|
||||
if (obj.middlename) {
|
||||
return `${obj.firstname} ${obj.middlename} ${obj.lastname}`.toLowerCase().includes(value.toLowerCase())
|
||||
}
|
||||
return `${obj.firstname} ${obj.lastname}`.toLowerCase().includes(value.toLowerCase())
|
||||
};
|
||||
|
||||
export const filterAddress = (row, columnId, value) => {
|
||||
const obj = row.original.address;
|
||||
if (obj.address2) {s
|
||||
return `${obj.address1} ${obj.address2} ${obj.postalcode} ${obj.city} ${obj.country}`.toLowerCase().includes(value.toLowerCase())
|
||||
}
|
||||
return `${obj.address1} ${obj.postalcode} ${obj.city} ${obj.country}`.toLowerCase().includes(value.toLowerCase())
|
||||
};
|
||||
|
||||
export const statusFilter = (row, columnId, value) => {
|
||||
const status = row.getValue(columnId);
|
||||
return status.toString().includes(value);
|
||||
};
|
@@ -1,7 +1,7 @@
|
||||
<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;
|
||||
@@ -66,7 +66,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -1,8 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
import { ScanStationService } from "@odit/lfk-client-js";
|
||||
import { StatsClientService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let modal_open;
|
||||
@@ -13,10 +12,7 @@
|
||||
dispatch("cancelDelete", { id: delete_station.id });
|
||||
}
|
||||
function deleteClient() {
|
||||
ScanStationService.donorControllerRemove(
|
||||
delete_station.id,
|
||||
true
|
||||
)
|
||||
StatsClientService.statsClientControllerRemove(delete_station.id, true)
|
||||
.then((resp) => {
|
||||
Toastify({
|
||||
text: $_('statsclient-deleted'),
|
||||
@@ -32,7 +28,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={cancelDelete}>
|
||||
<div
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<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;
|
||||
@@ -46,70 +46,87 @@
|
||||
{#if valueCopy != null}
|
||||
<textarea bind:this={areaDom}>{valueCopy}</textarea>
|
||||
{/if}
|
||||
<div class="fixed z-10 inset-0 overflow-y-auto" use:focusTrap>
|
||||
<div class="fixed z-10 inset-0 overflow-y-auto">
|
||||
<div
|
||||
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
|
||||
>
|
||||
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
||||
<div
|
||||
class="absolute inset-0 bg-gray-500 opacity-75"
|
||||
data-id="modal_backdrop" />
|
||||
data-id="modal_backdrop"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
class="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
aria-hidden="true">​</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">
|
||||
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">
|
||||
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" />
|
||||
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>
|
||||
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')}
|
||||
{$_("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')}
|
||||
{$_(
|
||||
"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')}
|
||||
{$_("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">
|
||||
class="block text-sm font-medium text-gray-700"
|
||||
>{$_("token")}</label
|
||||
>
|
||||
<button 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">
|
||||
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">
|
||||
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" />
|
||||
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>
|
||||
d="M7 4V2h10v2h3l1 1v16a1 1 0 01-1 1H4a1 1 0 01-1-1V5l1-1h3zm0 2H5v14h14V6h-2v2H7V6zm2-2v2h6V4H9z"
|
||||
/></svg
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<p class="text-gray-500 text-xs">
|
||||
{$_('click-to-copy-token-to-clipboard')}
|
||||
{$_("click-to-copy-token-to-clipboard")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -119,8 +136,9 @@
|
||||
<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')}
|
||||
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>
|
||||
|
@@ -41,7 +41,7 @@
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
class="mb-4" />
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||
<table class="divide-y divide-gray-200 w-full">
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import {
|
||||
RunnerOrganizationService,
|
||||
RunnerTeamService,
|
||||
@@ -77,7 +77,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { RunnerTeamService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
@@ -29,7 +29,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={cancelDelete}>
|
||||
<div
|
||||
|
@@ -57,7 +57,7 @@
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
class="mb-4" />
|
||||
<div class="h-12">
|
||||
<GenerateSponsoringContracts
|
||||
bind:sponsoring_contracts_show
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { tracks as tracksstore } from "../../store.js";
|
||||
import { TrackService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
@@ -75,7 +75,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "../base/outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
|
||||
import { UserService } from "@odit/lfk-client-js";
|
||||
import isEmail from "validator/es/lib/isEmail";
|
||||
import Toastify from "toastify-js";
|
||||
@@ -92,7 +92,7 @@
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
|
||||
use:clickOutside
|
||||
on:click_outside={() => {
|
||||
modal_open = false;
|
||||
|
@@ -36,7 +36,7 @@
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
class="mb-4" />
|
||||
<!-- {/if} -->
|
||||
<!-- <button
|
||||
on:click={() => {
|
||||
@@ -82,14 +82,6 @@
|
||||
<tr data-rowid="user_{u.id}">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
{#if u.profilePic}
|
||||
<div class="flex-shrink-0 h-10 w-10">
|
||||
<img
|
||||
class="h-10 w-10 rounded-full"
|
||||
src={u.profilePic}
|
||||
alt="" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{u.firstname}
|
||||
|
@@ -30,17 +30,22 @@
|
||||
"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-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-runners-will-be-deleted-too": "Alle zugehörigen Läufer:innen werden auch gelöscht!",
|
||||
"all-associated-scans-will-get-deleted-as-well": "Alle Scans dieser Station werden ebenfalls gelöscht",
|
||||
"all-associated-teams-and-runners-will-be-deleted-too": "Alle assoziierten Teams und Läufer:innen werden auch gelöscht!",
|
||||
"already-paid": "Bereits bezahlt",
|
||||
"amount": "Anzahl",
|
||||
"amount-per-kilometer": "Betrag pro Kilometer",
|
||||
"apartment-suite-etc": "Apartment, Wohnung, etc.",
|
||||
"api-endpoint": "API-Endpunkt",
|
||||
"application_name": "Lauf für Kaya! - Admin",
|
||||
"applying-changes": "Änderungen anwenden",
|
||||
"attention": "Achtung!",
|
||||
"author": "Autor:in",
|
||||
"average-distance": "Durchschnittliche Strecke/Läufer:in",
|
||||
"average-donation": "Durchschnittliches Sponsoring",
|
||||
"bitte-bestaetige-diese-laeufer-fuer-den-import": "Bitte die Läufer:innen für den Import bestätigen.",
|
||||
"by": "von",
|
||||
"cancel": "Abbrechen",
|
||||
@@ -48,13 +53,16 @@
|
||||
"cancel-keep-donor": "Abbrechen, Sponsor:in behalten",
|
||||
"cancel-keep-my-profile": "Abbrechen, mein Profil behalten",
|
||||
"cancel-keep-organization": "Abbrechen und Organisation bearbeiten",
|
||||
"cancel-keep-station": "Abbrechen und Station behalten",
|
||||
"cancel-keep-statsclient": "Abbrechen und Statsclient 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.",
|
||||
"card": "Läuferkarte",
|
||||
"card-added": "Karte wurde hinzugefügt",
|
||||
"card-deleted": "Karte gelöscht",
|
||||
"card-updated": "Karte aktualisiert",
|
||||
"cards": "Läuferkarten",
|
||||
"cards-deleted": "Karten gelöscht",
|
||||
"certificates": "Urkunden",
|
||||
"change-your-password-here": "Hier kannst du dein Passwort ändern",
|
||||
"changing-your-password": "Passwort wird geändert",
|
||||
@@ -63,12 +71,14 @@
|
||||
"click-to-copy-token-to-clipboard": "Klicke auf den Token, um ihn in deine Zwischenablage zu kopieren",
|
||||
"close": "Schließen",
|
||||
"code": "Code",
|
||||
"config-codes": "Konfigurations-Codes",
|
||||
"configure-the-tracks-and-minimum-lap-times": "Bearbeite die Tracks und ihre minimale Rundenzeit",
|
||||
"confirm": "Bestätigen",
|
||||
"confirm-delete": "Löschung Bestätigen",
|
||||
"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-organization-and-associated-teams-runners": "Bestätugung, lösche die Organisation und alle zugehörigen Teams und Läufer:innen.",
|
||||
"confirm-delete-station-with-all-scans": "Löschen der Scannerstation mit allen Scans bestätigen",
|
||||
"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-deletion": "Löschung Bestätigen",
|
||||
@@ -137,6 +147,7 @@
|
||||
"sort_column_descending": "Spalte absteigend sortieren"
|
||||
},
|
||||
"delete": "Löschen",
|
||||
"delete-cards": "Karten löschen",
|
||||
"delete-contact": "Kontakt löschen",
|
||||
"delete-donation": "Sponsoring löschen",
|
||||
"delete-donor": "Sponsor:in löschen",
|
||||
@@ -145,6 +156,7 @@
|
||||
"delete-profile": "Profil löschen",
|
||||
"delete-runner": "Läufer:in löschen",
|
||||
"delete-scan": "Scan löschen",
|
||||
"delete-scans": "Scans löschen",
|
||||
"delete-station": "Station löschen",
|
||||
"delete-statsclient": "Statsclient löschen",
|
||||
"delete-team": "Team Löschen",
|
||||
@@ -171,10 +183,11 @@
|
||||
"donation-updated": "Sponsoring wurde aktualisiert",
|
||||
"donation_added": "Sponsoring hinzugefügt",
|
||||
"donations": "Sponsorings",
|
||||
"donations-are-being-loaded": "Sponsorings werden geladen...",
|
||||
"donor": "Sponsor:in",
|
||||
"donor-added": "Sponsor:in hinzugefügt",
|
||||
"donor-deleted": "Sponsor:in gelöscht",
|
||||
"donor-has-no-associated-donations": "Zur Sponsor:in gibt es noch keine Sponsorings",
|
||||
"donor-has-no-associated-donations": "Keine Sponsorings",
|
||||
"donor-is-being-added": "Sponsor:in wird hinzugefügt...",
|
||||
"donor-is-being-updated": "Sponsor:in wird aktualisiert",
|
||||
"donors": "Sponsor:innen",
|
||||
@@ -273,6 +286,7 @@
|
||||
"name": "Name",
|
||||
"name-is-required": "Der Gruppenname muss angegeben werden",
|
||||
"new-password": "Neues Passwort",
|
||||
"no-address": "Keine Adresse hinterlegt",
|
||||
"no-contact-found": "Keine Kontakte gefunden",
|
||||
"no-contact-selected": "Kein Kontakt ausgewählt",
|
||||
"no-contact-specified": "Kein Kontakt angegeben",
|
||||
@@ -313,6 +327,10 @@
|
||||
"permissions": "Berechtigungen",
|
||||
"permissions-updated": "Berechtigungen aktualisiert!",
|
||||
"phone": "Telefon",
|
||||
"please-confirm-the-deletion-of-card": "Bitte bestätige die Löschung der Karte",
|
||||
"please-confirm-the-deletion-of-donation": "Bitte bestätige die Löschung des Sponsorings",
|
||||
"please-confirm-the-deletion-of-runner": "Bitte bestätige die Löschung der Läufer:in",
|
||||
"please-confirm-the-deletion-of-scan": "Bitte bestätige die Löschung des Scans",
|
||||
"please-copy-the-token-and-store-it-somewhere-save": "Bitte kopiere dir den Token und bewahre ihn gut auf.",
|
||||
"please-provide-a-password": "Bitte gebe ein Passwort an...",
|
||||
"please-provide-the-nessecary-information-to-add-a-new-donor": "Bitte mach die Notwendigen Angaben, um eine neue Sponsor:in zu erstellen",
|
||||
@@ -345,6 +363,7 @@
|
||||
"reset-password": "Passwort zurücksetzen",
|
||||
"runner": "Läufer:in",
|
||||
"runner-added": "Läufer:in hinzugefügt",
|
||||
"runner-deleted": "Läufer:in gelöscht",
|
||||
"runner-import": "Läufer:innen Import",
|
||||
"runner-is-being-added": "Läufer:in wird hinzugefügt...",
|
||||
"runner-updated": "Läufer:in aktualisiert!",
|
||||
@@ -356,6 +375,7 @@
|
||||
"save": "Speichern",
|
||||
"save-changes": "Änderungen speichern",
|
||||
"scan-added": "Scan hinzugefügt",
|
||||
"scan-deleted": "Scan gelöscht",
|
||||
"scan-is-being-updated": "Scan wird aktualisiert",
|
||||
"scan-with-fixed-distance": "Scan mit Festdistanz",
|
||||
"scans": "Scans",
|
||||
@@ -380,6 +400,7 @@
|
||||
"something-about-the-group": "Infos zur Gruppe",
|
||||
"sponsoring-quittungs-liste_herunterladen": "Sponsoring-Quittungs-Liste herunterladen",
|
||||
"sponsorings": "Sponsoringerklaerungen",
|
||||
"station-deleted": "Scannerstation gelöscht",
|
||||
"stats-are-being-loaded": "Die Statistiken werden geladen...",
|
||||
"statsclient-deleted": "Statsclient wurde gelöscht",
|
||||
"statsclient-is-being-added": "Statsclient wird angelegt...",
|
||||
@@ -414,11 +435,14 @@
|
||||
"this-card-is": "Diese Karte ist",
|
||||
"this-might-take-a-moment": "Das könnte einen kleinen Moment dauern",
|
||||
"this-scanstation-is": "Diese Station ist",
|
||||
"timestamp": "Timestamp",
|
||||
"token": "Token",
|
||||
"total-distance": "gelaufene Strecke",
|
||||
"total-donation-amount": "Gesamtbetrag",
|
||||
"total-donation-count": "Gesamte Sponsorings",
|
||||
"total-donations": "Spendensumme",
|
||||
"total-paid-amount": "Gezahlter Gesamtbetrag",
|
||||
"total-donors": "gesamte Sponsor:innen",
|
||||
"total-paid-amount": "Gezahlt",
|
||||
"total-scans": "gesamte Scans",
|
||||
"total_donation_amount_in_eur": "Gesamtbetrag in €",
|
||||
"track": "Track",
|
||||
|
@@ -30,17 +30,22 @@
|
||||
"address-is-required": "Address is required",
|
||||
"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.",
|
||||
"all": "all",
|
||||
"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-scans-will-get-deleted-as-well": "All associated scans will get deleted as well",
|
||||
"all-associated-teams-and-runners-will-be-deleted-too": "All associated teams and runners will be deleted too!",
|
||||
"already-paid": "Already paid",
|
||||
"amount": "Amount",
|
||||
"amount-per-kilometer": "Amount per kilometer",
|
||||
"apartment-suite-etc": "Apartment, suite, etc.",
|
||||
"api-endpoint": "API-Endpoint",
|
||||
"application_name": "Lauf für Kaya! - Admin",
|
||||
"applying-changes": "Applying Changes",
|
||||
"attention": "Attention!",
|
||||
"author": "Author",
|
||||
"average-distance": "average distance",
|
||||
"average-donation": "average donation",
|
||||
"bitte-bestaetige-diese-laeufer-fuer-den-import": "Please confirm these runners for import.",
|
||||
"by": "by",
|
||||
"cancel": "Cancel",
|
||||
@@ -48,13 +53,16 @@
|
||||
"cancel-keep-donor": "Cancel, keep donor",
|
||||
"cancel-keep-my-profile": "Cancel, keep my profile",
|
||||
"cancel-keep-organization": "Cancel, keep organization",
|
||||
"cancel-keep-station": "Cancel, keep station",
|
||||
"cancel-keep-statsclient": "Cancel and keep statsclient",
|
||||
"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",
|
||||
"card": "card",
|
||||
"card-added": "Card added",
|
||||
"card-deleted": "Card deleted",
|
||||
"card-updated": "Card updated",
|
||||
"cards": "Cards",
|
||||
"cards-deleted": "Cards deleted",
|
||||
"certificates": "Certificates",
|
||||
"change-your-password-here": "Change your password here",
|
||||
"changing-your-password": "Changing your password",
|
||||
@@ -63,12 +71,14 @@
|
||||
"click-to-copy-token-to-clipboard": "Click to copy the token to your clipboard",
|
||||
"close": "Close",
|
||||
"code": "Code",
|
||||
"config-codes": "Config codes",
|
||||
"configure-the-tracks-and-minimum-lap-times": "configure the tracks & minimum lap times",
|
||||
"confirm": "Confirm",
|
||||
"confirm-delete": "Confirm Delete",
|
||||
"confirm-delete-donor-with-all-donations": "Confirm, delete donor with all donations",
|
||||
"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-station-with-all-scans": "Confirm deletion of station with all scans",
|
||||
"confirm-delete-statsclient": "Confirm, delete statsclient",
|
||||
"confirm-delete-team-and-associated-runners": "Confirm, delete team and associated runners.",
|
||||
"confirm-deletion": "Confirm Deletion",
|
||||
@@ -137,6 +147,7 @@
|
||||
"an_error_happened_while_fetching_the_data": "An error happened while fetching the data"
|
||||
},
|
||||
"delete": "Delete",
|
||||
"delete-cards": "Delete cards",
|
||||
"delete-contact": "Delete Contact",
|
||||
"delete-donation": "Delete Donation",
|
||||
"delete-donor": "Delete donor",
|
||||
@@ -145,6 +156,7 @@
|
||||
"delete-profile": "Delete Profile",
|
||||
"delete-runner": "Delete Runner",
|
||||
"delete-scan": "Delete scan",
|
||||
"delete-scans": "Delete scans",
|
||||
"delete-station": "Delete station",
|
||||
"delete-statsclient": "Delete statsclient",
|
||||
"delete-team": "Delete Team",
|
||||
@@ -171,10 +183,11 @@
|
||||
"donation-updated": "Donation updated",
|
||||
"donation_added": "Donation_added",
|
||||
"donations": "Donations",
|
||||
"donations-are-being-loaded": "donations are being loaded",
|
||||
"donor": "Donor",
|
||||
"donor-added": "Donor added",
|
||||
"donor-deleted": "donor deleted",
|
||||
"donor-has-no-associated-donations": "Donor has no associated donations.",
|
||||
"donor-has-no-associated-donations": "No donations",
|
||||
"donor-is-being-added": "Donor is being added...",
|
||||
"donor-is-being-updated": "Donor is being updated",
|
||||
"donors": "Donors",
|
||||
@@ -273,6 +286,7 @@
|
||||
"name": "Name",
|
||||
"name-is-required": "Name is required",
|
||||
"new-password": "New password",
|
||||
"no-address": "no address",
|
||||
"no-contact-found": "No contacts found",
|
||||
"no-contact-selected": "No contact selected",
|
||||
"no-contact-specified": "no contact specified",
|
||||
@@ -313,6 +327,10 @@
|
||||
"permissions": "Permissions",
|
||||
"permissions-updated": "Permissions updated!",
|
||||
"phone": "Phone",
|
||||
"please-confirm-the-deletion-of-card": "Please confirm the deletion of this card",
|
||||
"please-confirm-the-deletion-of-donation": "Please confirm the deletion of this donation",
|
||||
"please-confirm-the-deletion-of-runner": "Please confirm the deletion of this runner",
|
||||
"please-confirm-the-deletion-of-scan": "Please confirm the deletion of scan",
|
||||
"please-copy-the-token-and-store-it-somewhere-save": "Please copy the token and store it somewhere safe.",
|
||||
"please-provide-a-password": "Please provide a password...",
|
||||
"please-provide-the-nessecary-information-to-add-a-new-donor": "Please provide the nessecary information to add a new donor",
|
||||
@@ -345,6 +363,7 @@
|
||||
"reset-password": "Reset your password",
|
||||
"runner": "Runner",
|
||||
"runner-added": "Runner added",
|
||||
"runner-deleted": "runner deleted",
|
||||
"runner-import": "Runner Import",
|
||||
"runner-is-being-added": "Runner is being added...",
|
||||
"runner-updated": "Runner updated!",
|
||||
@@ -356,6 +375,7 @@
|
||||
"save": "Save",
|
||||
"save-changes": "Save Changes",
|
||||
"scan-added": "Scan added",
|
||||
"scan-deleted": "scan deleted",
|
||||
"scan-is-being-updated": "Scan is being updated",
|
||||
"scan-with-fixed-distance": "Scan with fixed distance",
|
||||
"scans": "Scans",
|
||||
@@ -380,6 +400,7 @@
|
||||
"something-about-the-group": "Something about the group...",
|
||||
"sponsoring-quittungs-liste_herunterladen": "Download donor receipt list",
|
||||
"sponsorings": "Sponsorings",
|
||||
"station-deleted": "station deleted",
|
||||
"stats-are-being-loaded": "stats are being loaded...",
|
||||
"statsclient-deleted": "Deleted statsclient",
|
||||
"statsclient-is-being-added": "Statsclient is being added...",
|
||||
@@ -414,11 +435,14 @@
|
||||
"this-card-is": "This card is",
|
||||
"this-might-take-a-moment": "This might take a moment 👀",
|
||||
"this-scanstation-is": "This scanstation is",
|
||||
"timestamp": "timestamp",
|
||||
"token": "Token",
|
||||
"total-distance": "total distance",
|
||||
"total-donation-amount": "total donation amount",
|
||||
"total-donation-count": "total donations (count)",
|
||||
"total-donations": "total donations",
|
||||
"total-paid-amount": "Total paid amount",
|
||||
"total-donors": "total donors",
|
||||
"total-paid-amount": "Paid",
|
||||
"total-scans": "total scans",
|
||||
"total_donation_amount_in_eur": "Total donation amount in €",
|
||||
"track": "Track",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
module.exports = {
|
||||
mode: 'jit',
|
||||
purge: [ './src/**/*.svelte' ],
|
||||
content: [ './src/**/*.svelte' ],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
const fs = require('fs');
|
||||
const package = JSON.parse(fs.readFileSync(`./package.json`, { encoding: 'utf-8' }));
|
||||
import fs from 'fs';
|
||||
const packagejson = JSON.parse(fs.readFileSync(`./package.json`, { encoding: 'utf-8' }));
|
||||
const original = fs.readFileSync(`./index.html`, { encoding: 'utf-8' });
|
||||
let out = original.replace(/RELEASE_INFO-(\S)+-RELEASE_INFO/gi, 'RELEASE_INFO-' + package.version + '-RELEASE_INFO');
|
||||
let out = original.replace(/RELEASE_INFO-(\S)+-RELEASE_INFO/gi, 'RELEASE_INFO-' + packagejson.version + '-RELEASE_INFO');
|
||||
fs.writeFileSync(`./index.html`, out);
|
||||
|
@@ -1,38 +1,11 @@
|
||||
import svelte from '@sveltejs/vite-plugin-svelte';
|
||||
import { minify } from 'html-minifier';
|
||||
// vite.config.js
|
||||
import { defineConfig } from 'vite';
|
||||
//
|
||||
const indexReplace = () => {
|
||||
return {
|
||||
name: 'html-transform',
|
||||
transformIndexHtml(html) {
|
||||
return minify(html, {
|
||||
collapseWhitespace: true
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
const isProduction = mode === 'production';
|
||||
return {
|
||||
// base: './',
|
||||
build: {
|
||||
polyfillDynamicImport: false,
|
||||
cssCodeSplit: false,
|
||||
minify: isProduction
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
//@ts-ignore
|
||||
hot: !isProduction,
|
||||
emitCss: true,
|
||||
extensions: [ '.md', '.svx', '.svelte' ],
|
||||
preprocess: [
|
||||
//
|
||||
]
|
||||
}),
|
||||
indexReplace()
|
||||
]
|
||||
};
|
||||
});
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
svelte({
|
||||
/* plugin options */
|
||||
})
|
||||
]
|
||||
});
|
Reference in New Issue
Block a user