Compare commits

...

141 Commits

Author SHA1 Message Date
087c85e586 🚀RELEASE v0.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-12 19:18:33 +01:00
43df188df1 new license file version [CI SKIP] 2021-02-12 18:16:52 +00:00
1d40c6d068 Merge branch 'dev' of https://git.odit.services/lfk/frontend into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-12 19:15:41 +01:00
6614242df6 dependency bump 2021-02-12 19:15:34 +01:00
b25dc623cf new license file version [CI SKIP] 2021-02-12 18:10:08 +00:00
9d3bb4e4e3 Merge pull request 'feature/52-runner-filters' (#63) from feature/52-runner-filters into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #63
close #52
2021-02-12 18:09:21 +00:00
5dc11c285d RunnersOverview - support should_display_based_on_id search
ref #52
2021-02-12 19:06:31 +01:00
575b4ce970 RunnersOverview - basic working filter
ref #52
2021-02-12 18:47:46 +01:00
e23821a7cb WIP on filter
ref #52
2021-02-12 18:04:10 +01:00
e415258787 basic select filtering in RunnersOverview
ref #52
2021-02-09 19:22:20 +01:00
1451991d03 Merge pull request 'feature/43-password-reset' (#61) from feature/43-password-reset into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #61
close #43
2021-02-07 16:11:16 +00:00
92fee08dc4 ⏮decode base64 reset key 2021-02-07 13:26:57 +01:00
7b7e484091 🧹 formatting 2021-02-07 13:25:39 +01:00
e7291a31f3 Merge pull request 'feature/51-teamoverview-badge-org' (#59) from feature/51-teamoverview-badge-org into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #59
close #51
2021-02-07 12:12:57 +00:00
2dc31912cc Merge pull request 'feature/47-sidebar-responsiveness' (#60) from feature/47-sidebar-responsiveness into dev
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #60
close #47
2021-02-07 12:12:29 +00:00
428a8a10ff 🌍 i18n for ResetPassword
ref #43
2021-02-07 13:11:08 +01:00
8b2f1965e2 👀 ResetPassword - success and error states
ref #43
2021-02-07 13:08:44 +01:00
b18a99e4df added basic UI for ResetPassword
ref #43
2021-02-07 12:51:21 +01:00
1fac0c8640 😬 use actual team id for RunnersOverview badge
ref #51
2021-02-07 12:18:34 +01:00
f0be73c2cd Merge commit '65f49489ad2e0cff30560e4c326ca7294d7f6190' into feature/51-teamoverview-badge-org 2021-02-07 12:11:09 +01:00
42bd632539 👀 only display navbar on sm devices / hide on md and up 2021-02-07 12:07:25 +01:00
f8014c6213 🐞 re-add sidebar component 2021-02-07 12:06:54 +01:00
65f49489ad new license file version [CI SKIP] 2021-02-07 11:05:54 +00:00
e9e24d5f2d Merge branch 'dev' of https://git.odit.services/lfk/frontend into dev
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-07 12:05:08 +01:00
369b09972c general dependency bump 2021-02-07 12:04:55 +01:00
e4e2bdac72 new license file version [CI SKIP] 2021-01-30 16:58:42 +00:00
ec0db39184 Merge pull request 'feature/15-runner-import' (#58) from feature/15-runner-import into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #58
close #15
2021-01-30 16:57:59 +00:00
a02be78df5 RunnersOverview badge to team/org
ref #51
2021-01-30 17:56:58 +01:00
3490abe9a7 Org badge in TeamsOverview
ref #51
2021-01-30 17:56:40 +01:00
b5013426e6 reactivity in import table
ref #15
2021-01-30 17:50:16 +01:00
7dd0881d29 working import binding
ref #15
2021-01-30 17:45:12 +01:00
acf0562851 ImportRunnerModal - differenciate between team and org import
ref #15
2021-01-30 17:27:51 +01:00
80c3a90d6f client library bump
ref #15
2021-01-30 17:14:20 +01:00
c6985087a8 layout for Import from RunnerOverview
ref #15
2021-01-30 17:05:19 +01:00
ef50e2d5ce Merge pull request 'feature/56-footer-version-loading' (#57) from feature/56-footer-version-loading into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #57
close #56
2021-01-30 15:33:24 +00:00
ed1dc6e8d5 Merge branch 'dev' into feature/56-footer-version-loading 2021-01-30 15:28:07 +00:00
c2d7a319a0 use onMount event instead of DOMready
ref #56
2021-01-30 16:24:48 +01:00
0e04298b7c new license file version [CI SKIP] 2021-01-30 15:17:37 +00:00
3a5a73b02c Merge pull request 'feature/46-imprint-privacy' (#55) from feature/46-imprint-privacy into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #55
close #46
2021-01-30 15:16:46 +00:00
901c7c1241 load version on DOMContentLoaded
ref #56
2021-01-30 16:16:30 +01:00
4e82369b16 text selection color
ref #46
2021-01-30 16:14:38 +01:00
945963db32 Footer linking
ref #46
2021-01-29 19:07:46 +01:00
5741cbe756 added Privacy page
ref #46
2021-01-29 19:02:53 +01:00
6401aeb3a8 working Imprint page
ref #46
2021-01-29 18:49:02 +01:00
6a0c129d39 added basic styling to Imprint component
ref #46
2021-01-29 17:58:52 +01:00
bbec9ffcdf added Imprint route /imprint
ref #46
2021-01-29 17:58:22 +01:00
541f1fa2e3 Merge pull request 'feature/44-require-mail-addresses' (#54) from feature/44-require-mail-addresses into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #54
close #44
2021-01-29 15:38:06 +00:00
7897820632 UserDetail - invalid email UI feedback
ref #44
2021-01-29 16:34:55 +01:00
0dd9de2abc UserDetail - enforce email input
ref #44
2021-01-27 18:48:23 +01:00
7131ba99b6 AddUserModal - enforce email input
ref #44
2021-01-27 18:45:36 +01:00
c69026aa5b Merge pull request 'feature/45-component-drop' (#53) from feature/45-component-drop into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #53
close #45
2021-01-27 17:42:08 +00:00
16ac96c64e 🧹 remove placeholder options from Dashboard sidebar
ref #45
2021-01-27 18:26:46 +01:00
1bb79b1c98 🧹 remove ComponentDump from MainDashContent
ref #45
2021-01-27 18:26:26 +01:00
eeee272f03 🧹 drop old + unused components Dash + LoginAlt
ref #45
2021-01-27 18:26:04 +01:00
2563e9d1d6 🚀RELEASE v0.5.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-26 18:29:39 +01:00
24bec66390 🐞 fix package release for index template compatibility 2021-01-26 18:29:26 +01:00
3d30734dc2 new license file version [CI SKIP] 2021-01-26 17:26:12 +00:00
cbc1d53cc2 🐞 fixed merge conflict errors
All checks were successful
continuous-integration/drone/push Build is passing
ref #13 #12
2021-01-26 18:25:30 +01:00
9a1f7a13f8 general dependency bump 2021-01-26 18:25:02 +01:00
d48b9b7f91 Merge pull request 'feature/12-user-management' (#39) from feature/12-user-management into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #39
close #12
2021-01-26 16:28:59 +00:00
358865dc6a UserDetail - update permission badges on change save
ref #12
2021-01-26 17:28:07 +01:00
e750c37473 🌎 UserDetail - more i18n keys
ref #12
2021-01-26 17:27:44 +01:00
71a589c10c Merge branch 'dev' into feature/12-user-management
# Conflicts:
#	src/App.svelte
2021-01-26 17:00:21 +01:00
3281239ff5 new license file version [CI SKIP] 2021-01-26 15:59:14 +00:00
3e2cdddd5a Merge pull request 'feature/13-runner-management' (#42) from feature/13-runner-management into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #42
close #13
2021-01-26 15:58:28 +00:00
1d4c3e51c7 Merge branch 'dev' into feature/13-runner-management 2021-01-26 15:57:55 +00:00
1694c71528 AddRunnerModal - improved team select
ref #13
2021-01-26 16:54:15 +01:00
169ffc1b0b 🌎 added more i18n keys
ref #13
2021-01-26 16:53:59 +01:00
89eb3259d4 Merge pull request 'added permissions to dashboard sidebar' (#41) from feature/40-dynamic-sidebar-options into dev
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #41
close #41
2021-01-26 15:42:02 +00:00
ffd88ffc66 🐞 alphabetically sort permission targets in UserDetail
ref #39 ref #12
2021-01-25 20:56:20 +01:00
6c2d13bd17 🌎 last i18n keys
ref #13
2021-01-25 20:39:34 +01:00
8d92a75ef0 🏃‍♂️ support for runner group edit
ref #13
2021-01-25 20:39:22 +01:00
acb86ae266 added permissions to dashboard sidebar
ref #40
2021-01-25 20:20:58 +01:00
473cf978b4 Merge branch 'dev' into feature/12-user-management
# Conflicts:
#	src/components/UserDetail.svelte
2021-01-24 21:50:38 +01:00
4debab2636 Merge branch 'dev' into feature/13-runner-management
# Conflicts:
#	package.json
2021-01-24 20:39:11 +01:00
e91e197873 new license file version [CI SKIP] 2021-01-24 19:39:09 +00:00
b30b6734a1 general dependency bump
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-24 20:38:29 +01:00
dadb80608a 💣 process breaking changes for lib v0.3.0
Some checks failed
continuous-integration/drone/push Build is failing
2021-01-24 20:37:56 +01:00
16c9969fb9 Merge branch 'feature/13-runner-management' of https://git.odit.services/lfk/frontend into feature/13-runner-management 2021-01-24 18:41:04 +01:00
dc1644ed25 🐞 ImportRunnerModal - table overflow fix
ref #13
2021-01-24 18:40:54 +01:00
2cc9b3e1ed 👀 ImportRunnerModal - layout cleanup
ref #13
2021-01-24 18:38:16 +01:00
415f340a68 i18n 🌍
ref #13
2021-01-24 18:20:48 +01:00
2d4869128d 🌎 i18n
ref #13
2021-01-24 17:54:45 +01:00
ae8bc01d9b 🌎 i18n
ref #13
2021-01-24 17:47:29 +01:00
e2552d9442 Merge branch 'dev' into feature/13-runner-management 2021-01-24 17:41:11 +01:00
5d1b5d80b6 Merge branch 'feature/13-runner-management' of https://git.odit.services/lfk/frontend into feature/13-runner-management 2021-01-24 17:31:31 +01:00
366804aa29 🌎 i18n
ref #13
2021-01-24 17:31:10 +01:00
9240e0c903 🧹 general runner cleanup
ref #13
2021-01-24 17:30:31 +01:00
7baaf2cff3 Merge branch 'feature/13-runner-management' of git.odit.services:lfk/frontend into feature/13-runner-management 2021-01-24 17:23:18 +01:00
9fbc1ba031 Added friles to ignore 2021-01-24 17:21:48 +01:00
6704c07db0 Deleted files to ignore 2021-01-24 17:21:39 +01:00
a87165148a 🧹 RunnerDetail cleanup + i18n 🌎
ref #13
2021-01-24 17:21:19 +01:00
ec4bcd093b 🐞 [bugfix] RunnerDetail update
ref #13
2021-01-24 17:16:49 +01:00
5552055b98 RunnerDetail - button text fixes
ref #13
2021-01-21 20:50:14 +01:00
03aa67034d basic RunnerDetail
ref #13
2021-01-21 19:07:43 +01:00
fc21427685 💻 updated VSCode recommended extensions
All checks were successful
continuous-integration/drone/push Build is passing
change i18n-ally scoping
2021-01-21 18:41:05 +01:00
819b02a204 ImportRunnerModal - hide team when loading from TeamDetail
ref #13
2021-01-20 20:25:41 +01:00
de0bd5fd57 ImportRunnerModal usage in TeamDetail
ref #13
2021-01-20 20:21:59 +01:00
8aa1d94a1a use ImportRunnerModal in OrgDetail + Orgs
ref #13
2021-01-20 19:59:53 +01:00
f8a59133a2 ImportRunnerModal - compatibility for multi-component access
ref #13
2021-01-20 19:59:31 +01:00
c382f770dc 👀 basic ui interaction for ImportRunnerModal
ref #13
2021-01-20 18:31:23 +01:00
cde4ec13ef basic cancel event dispatch from ImportRunnerModal
ref #13
2021-01-20 18:15:47 +01:00
ecad1ea839 ↙ added default fallback parsing to ImportRunnerModal
ref #13
2021-01-20 17:56:32 +01:00
6b91b22713 🚀 ImportRunnerModal - working demo
ref #13
2021-01-20 17:46:18 +01:00
e34c91b2cc 🌎 i18n in ImportRunnerModal headers
ref #13
2021-01-20 17:33:10 +01:00
822759a688 😦 added missing dependencies
ref #13
2021-01-19 21:45:00 +01:00
b606037890 🌎 added locale based csv/xlsx header parsing
ref #13
2021-01-19 21:44:41 +01:00
74d9b94119 basic xlsx + csv parsing
ref #13
2021-01-19 21:44:13 +01:00
b1e9f955b0 basic ImportRunnerModal with CSV input
ref #13
2021-01-19 18:19:21 +01:00
e0356fa360 fixed runner permissions
ref #13
2021-01-19 18:18:51 +01:00
fbc67eeb98 Merge branch 'dev' of https://git.odit.services/lfk/frontend into dev 2021-01-18 20:34:50 +01:00
09fd73b130 🐞 improved version builder from template 2021-01-18 20:34:47 +01:00
259a76f46b 🧹 darkmode classes cleanup 2021-01-18 20:29:44 +01:00
c6504c2eaf new license file version [CI SKIP] 2021-01-17 18:46:15 +00:00
7d104a1514 🚀RELEASE v0.4.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-17 19:45:28 +01:00
b3bd61c89e Merge branch 'feature/13-runner-management' into dev
Some checks failed
continuous-integration/drone/push Build was killed
2021-01-17 19:44:07 +01:00
e49dca0275 🐞 gitignore fix 2021-01-17 19:29:05 +01:00
03125b3a2d general dependency bumps 2021-01-17 19:24:57 +01:00
a523379b3a gitignore fix 2021-01-17 19:24:25 +01:00
aa6348a29a fix package:dev script
All checks were successful
continuous-integration/drone/push Build is passing
ref #37
2021-01-17 19:22:45 +01:00
b9f0f1a69a Merge commit 'a284806d3cb769030a4e28d0403190b746f8fc61' into dev
All checks were successful
continuous-integration/drone/push Build is passing
close #37
2021-01-17 19:18:42 +01:00
11790638d6 🔨 cleaned up build process + Dockerfile
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-17 19:01:11 +01:00
d4579a9a41 dynamic contact info in AddRunnerModal
ref #13
2021-01-17 17:54:21 +01:00
66a07c6a51 RunnersOverview
ref #13
2021-01-17 17:51:03 +01:00
66ffd8e936 AddRunnerModal
ref #13
2021-01-17 17:50:52 +01:00
44f07ca231 🧹 AddUserModal cleanup 2021-01-17 17:23:26 +01:00
ff14f024af AddUserModal - username/email validation
ref #12
2021-01-17 17:14:34 +01:00
10d7955f99 UserPermissions - working edit
ref #12
2021-01-16 21:42:09 +01:00
959b32de1c new UsersEmptyState
ref #12
2021-01-16 20:48:45 +01:00
9c6dc5b424 basic UserPermissions view
ref #12
2021-01-16 20:33:34 +01:00
aaab95d414 UserDetail - link to permission page
ref #12
2021-01-16 18:30:35 +01:00
7d4e93912c UserDetail - basic permission badges
ref #12
2021-01-16 18:23:28 +01:00
d65b463547 UserDetail - add permission layout
ref #12
2021-01-16 17:31:17 +01:00
6e5a4bcb39 formatting
ref #12
2021-01-16 17:19:03 +01:00
1a799dc30a Fixed User group update
ref #12
2021-01-16 17:17:56 +01:00
e3d2676858 Merge branch 'dev' into feature/12-user-management 2021-01-16 17:11:10 +01:00
65b36f8e69 🙋‍♂️🔒 UserDetail - permission layout ui
ref #12
2021-01-14 18:56:28 +01:00
87387a54f2 Merge branch 'dev' into feature/12-user-management 2021-01-14 18:30:08 +01:00
b34e3aeed0 👀 UsersOverview - disable advanced search
ref #12
2021-01-14 18:24:37 +01:00
86c54e04a8 🙋‍♂️ UserDetail - active/inactive user state edit
ref #12
2021-01-14 18:22:09 +01:00
ef9fc596f5 🙋‍♂️ UserDetail - disable profile picture edit
ref #12
2021-01-14 18:21:48 +01:00
46 changed files with 2237 additions and 2344 deletions

9
.gitignore vendored
View File

@@ -1,8 +1,11 @@
node_modules
public/env.js
build
yarn.lock
package-lock.json
yarn.lock
*.map
public/env.js
public/sw.js
public/index.html
public/workbox-*.js
svelte.config.js
public/index.html
svelte.config.js

View File

@@ -4,8 +4,10 @@
"christian-kohler.npm-intellisense",
"remimarsal.prettier-now",
"svelte.svelte-vscode",
"antfu.i18n-ally",
"lokalise.i18n-ally",
"fivethree.vscode-svelte-snippets"
],
"unwantedRecommendations": []
"unwantedRecommendations": [
"antfu.i18n-ally"
]
}

View File

@@ -2,8 +2,158 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [0.6.0](https://git.odit.services/lfk/frontend/compare/0.5.0...0.6.0)
- Merge pull request 'feature/52-runner-filters' (#63) from feature/52-runner-filters into dev [`#52`](https://git.odit.services/lfk/frontend/issues/52)
- Merge pull request 'feature/43-password-reset' (#61) from feature/43-password-reset into dev [`#43`](https://git.odit.services/lfk/frontend/issues/43)
- Merge pull request 'feature/51-teamoverview-badge-org' (#59) from feature/51-teamoverview-badge-org into dev [`#51`](https://git.odit.services/lfk/frontend/issues/51)
- Merge pull request 'feature/47-sidebar-responsiveness' (#60) from feature/47-sidebar-responsiveness into dev [`#47`](https://git.odit.services/lfk/frontend/issues/47)
- Merge pull request 'feature/15-runner-import' (#58) from feature/15-runner-import into dev [`#15`](https://git.odit.services/lfk/frontend/issues/15)
- Merge pull request 'feature/56-footer-version-loading' (#57) from feature/56-footer-version-loading into dev [`#56`](https://git.odit.services/lfk/frontend/issues/56)
- Merge pull request 'feature/46-imprint-privacy' (#55) from feature/46-imprint-privacy into dev [`#46`](https://git.odit.services/lfk/frontend/issues/46)
- Merge pull request 'feature/44-require-mail-addresses' (#54) from feature/44-require-mail-addresses into dev [`#44`](https://git.odit.services/lfk/frontend/issues/44)
- Merge pull request 'feature/45-component-drop' (#53) from feature/45-component-drop into dev [`#45`](https://git.odit.services/lfk/frontend/issues/45)
- 🧹 drop old + unused components Dash + LoginAlt [`eeee272`](https://git.odit.services/lfk/frontend/commit/eeee272f0353cd543dca0efbdf23f5972a771f6c)
- ✨ added basic UI for ResetPassword [`b18a99e`](https://git.odit.services/lfk/frontend/commit/b18a99e4df6111e137a8081c3e6619da6f5af30c)
- 🧹 remove placeholder options from Dashboard sidebar [`16ac96c`](https://git.odit.services/lfk/frontend/commit/16ac96c64e86a6f97e179d70b586f20869ffe663)
- 👀 ResetPassword - success and error states [`8b2f196`](https://git.odit.services/lfk/frontend/commit/8b2f1965e2a754da6e40a3051e8ae3e771d70336)
- added Privacy page [`5741cbe`](https://git.odit.services/lfk/frontend/commit/5741cbe7562542ba2b81a9a6d6be7fb0f5145801)
- ImportRunnerModal - differenciate between team and org import [`acf0562`](https://git.odit.services/lfk/frontend/commit/acf0562851a77b9122473ffb1753a94b4272e53b)
- ✨ RunnersOverview - basic working filter [`575b4ce`](https://git.odit.services/lfk/frontend/commit/575b4ce9708625fbec23c49101f44825c6a75bce)
- basic select filtering in RunnersOverview [`e415258`](https://git.odit.services/lfk/frontend/commit/e415258787c776d4a5291632f47c2fcceba9a040)
- WIP on filter [`e23821a`](https://git.odit.services/lfk/frontend/commit/e23821a7cbe73fda420e4bcaaa2dbf5a89b56cc9)
- ✨ layout for Import from RunnerOverview [`c698508`](https://git.odit.services/lfk/frontend/commit/c6985087a8b51d6e1d8fb71b1eb8a659b8ed34e6)
- ✨ working Imprint page [`6401aeb`](https://git.odit.services/lfk/frontend/commit/6401aeb3a850b1dd9ec4eca5c181ebcf3ed8b7e9)
- 🌍 i18n for ResetPassword [`428a8a1`](https://git.odit.services/lfk/frontend/commit/428a8a10ffa96ae2f04c718d678439d36e6cb857)
- ⏫ dependency bump [`6614242`](https://git.odit.services/lfk/frontend/commit/6614242df6a9679f60bc3996c0317d0870a81ac4)
- added Imprint route /imprint [`bbec9ff`](https://git.odit.services/lfk/frontend/commit/bbec9ffcdf0be2cf37501a355c5678ed9cd46a1d)
- ⏫ general dependency bump [`369b099`](https://git.odit.services/lfk/frontend/commit/369b09972c786e5381d7bae667def6a15e21a45b)
- AddUserModal - enforce email input [`7131ba9`](https://git.odit.services/lfk/frontend/commit/7131ba99b6917dc0fb99f93ab40495b9702024ab)
- Org badge in TeamsOverview [`3490abe`](https://git.odit.services/lfk/frontend/commit/3490abe9a70ca3bd1725ae005c4031f25996f2a7)
- RunnersOverview - support should_display_based_on_id search [`5dc11c2`](https://git.odit.services/lfk/frontend/commit/5dc11c285d4e2155a3945bc44cbfcf61fe02c5f3)
- RunnersOverview badge to team/org [`a02be78`](https://git.odit.services/lfk/frontend/commit/a02be78df52f944d5842f8d0e9bbf62ba95e9ef0)
- working import binding [`7dd0881`](https://git.odit.services/lfk/frontend/commit/7dd0881d293a7c46133cccd947e668178058ddd8)
- Footer linking [`945963d`](https://git.odit.services/lfk/frontend/commit/945963db326e937456103f0092bbc684b0d0ec45)
- UserDetail - invalid email UI feedback [`7897820`](https://git.odit.services/lfk/frontend/commit/7897820632c7b9e1e00fbf3f86a1eb7e01794fa5)
- reactivity in import table [`b501342`](https://git.odit.services/lfk/frontend/commit/b5013426e6d4b92f56a8b7a42f4f13411c2cf61c)
- 😬 use actual team id for RunnersOverview badge [`1fac0c8`](https://git.odit.services/lfk/frontend/commit/1fac0c8640460278bf6e2460e685e192e8a984f3)
- 🐞 re-add sidebar component [`f8014c6`](https://git.odit.services/lfk/frontend/commit/f8014c62132eaafaa5edb486f3839b9502f3287f)
- ⚡ load version on DOMContentLoaded [`901c7c1`](https://git.odit.services/lfk/frontend/commit/901c7c12418f09a29235afe8df5982f964fc61db)
- UserDetail - enforce email input [`0dd9de2`](https://git.odit.services/lfk/frontend/commit/0dd9de2abc68ce10c4c763382945b92c0cd4e404)
- use onMount event instead of DOMready [`c2d7a31`](https://git.odit.services/lfk/frontend/commit/c2d7a319a0761a77597fe365083b12c4c045a7ba)
- new license file version [CI SKIP] [`43df188`](https://git.odit.services/lfk/frontend/commit/43df188df149401c966b8d7bb6e28fbbe39a83f1)
- new license file version [CI SKIP] [`b25dc62`](https://git.odit.services/lfk/frontend/commit/b25dc623cf12aba88576cd8784a20b0fefc5466e)
- ⏮decode base64 reset key [`92fee08`](https://git.odit.services/lfk/frontend/commit/92fee08dc40cf04fa1dc6d6e4aaf19f0b752031f)
- 🧹 formatting [`7b7e484`](https://git.odit.services/lfk/frontend/commit/7b7e4840918b21951fa9e267044d623e9de7b0f5)
- Merge commit '65f49489ad2e0cff30560e4c326ca7294d7f6190' into feature/51-teamoverview-badge-org [`f0be73c`](https://git.odit.services/lfk/frontend/commit/f0be73c2cd6c930273515a8773a2affd4068c92c)
- 👀 only display navbar on sm devices / hide on md and up [`42bd632`](https://git.odit.services/lfk/frontend/commit/42bd63253916ab339a62f64762802750dc315b4c)
- new license file version [CI SKIP] [`65f4948`](https://git.odit.services/lfk/frontend/commit/65f49489ad2e0cff30560e4c326ca7294d7f6190)
- new license file version [CI SKIP] [`e4e2bda`](https://git.odit.services/lfk/frontend/commit/e4e2bdac724f40584bf1526fa59b0f380ba8cba0)
- ⏫ client library bump [`80c3a90`](https://git.odit.services/lfk/frontend/commit/80c3a90d6fd14f1946b1e4ecd5e19314ca952c2d)
- new license file version [CI SKIP] [`0e04298`](https://git.odit.services/lfk/frontend/commit/0e04298b7c53e2dfd447f51b75c7c32f0b730eea)
- text selection color [`4e82369`](https://git.odit.services/lfk/frontend/commit/4e82369b160da3eaa5a32cf104aacc6250e7e31e)
- added basic styling to Imprint component [`6a0c129`](https://git.odit.services/lfk/frontend/commit/6a0c129d39f5e659411c2358f30d425720229f16)
- 🧹 remove ComponentDump from MainDashContent [`1bb79b1`](https://git.odit.services/lfk/frontend/commit/1bb79b1c98d166523b638a06ea9a354434ec1e08)
#### [0.5.0](https://git.odit.services/lfk/frontend/compare/0.4.0...0.5.0)
> 26 January 2021
- Merge pull request 'feature/12-user-management' (#39) from feature/12-user-management into dev [`#12`](https://git.odit.services/lfk/frontend/issues/12)
- Merge pull request 'feature/13-runner-management' (#42) from feature/13-runner-management into dev [`#13`](https://git.odit.services/lfk/frontend/issues/13)
- Merge pull request 'added permissions to dashboard sidebar' (#41) from feature/40-dynamic-sidebar-options into dev [`#41`](https://git.odit.services/lfk/frontend/issues/41)
- basic UserPermissions view [`9c6dc5b`](https://git.odit.services/lfk/frontend/commit/9c6dc5b4247e76be63a92294556be1e4a9154256)
- ✨ basic RunnerDetail [`03aa670`](https://git.odit.services/lfk/frontend/commit/03aa67034d5fc4ec64d5646a6583666301bb538e)
- 👀 ImportRunnerModal - layout cleanup [`2cc9b3e`](https://git.odit.services/lfk/frontend/commit/2cc9b3e1ede47bb1a4b598afc260d0bc06ed0902)
- ✨ basic ImportRunnerModal with CSV input [`b1e9f95`](https://git.odit.services/lfk/frontend/commit/b1e9f955b054cd17cb83f8364b6bc2b22159142f)
- ✨ basic xlsx + csv parsing [`74d9b94`](https://git.odit.services/lfk/frontend/commit/74d9b94119b49a77ff3e787a84f76c1e2e13d9e4)
- 🧹 darkmode classes cleanup [`259a76f`](https://git.odit.services/lfk/frontend/commit/259a76f46b9ac01b177100dfce0db367b517b3a0)
- UserDetail - basic permission badges [`7d4e939`](https://git.odit.services/lfk/frontend/commit/7d4e93912c4e1fd8a875f42a0f11b906f1ca0091)
- added permissions to dashboard sidebar [`acb86ae`](https://git.odit.services/lfk/frontend/commit/acb86ae2666ec3c336973184ec5cb1e2ffba9ebe)
- UserPermissions - working edit [`10d7955`](https://git.odit.services/lfk/frontend/commit/10d7955f99b1a640eb1ac83764c114a25cfcc07b)
- 🐞 ImportRunnerModal - table overflow fix [`dc1644e`](https://git.odit.services/lfk/frontend/commit/dc1644ed25b82afe6a51fbe0e1de69ddf54ef81d)
- 🧹 general runner cleanup [`9240e0c`](https://git.odit.services/lfk/frontend/commit/9240e0c90380fc6b44a3dde480bd2d29476b2a0f)
- 🚀RELEASE v0.5.0 [`2563e9d`](https://git.odit.services/lfk/frontend/commit/2563e9d1d6fe55544b04de8edd8c24cc1d3c9735)
- ✨ formatting [`6e5a4bc`](https://git.odit.services/lfk/frontend/commit/6e5a4bcb39259fc376262c16ad95555fd9cf7a0b)
- ✨ ImportRunnerModal usage in TeamDetail [`de0bd5f`](https://git.odit.services/lfk/frontend/commit/de0bd5fd57edba75faaa2ba097a2c7fb2f621f9a)
- 💣 process breaking changes for lib v0.3.0 [`dadb806`](https://git.odit.services/lfk/frontend/commit/dadb80608a55980f7b647d41c91061055287a250)
- 👀 basic ui interaction for ImportRunnerModal [`c382f77`](https://git.odit.services/lfk/frontend/commit/c382f770dc05f57a471389d3d4d012fd8439c014)
- 🐞 [bugfix] RunnerDetail update [`ec4bcd0`](https://git.odit.services/lfk/frontend/commit/ec4bcd093b6c94f2e3597de6de98db5ca47c5876)
- 🏃‍♂️ support for runner group edit [`8d92a75`](https://git.odit.services/lfk/frontend/commit/8d92a75ef03854397a7c01ed9ea22967b994f9f2)
- AddRunnerModal - improved team select [`1694c71`](https://git.odit.services/lfk/frontend/commit/1694c71528f9114622daae1bd16aaed9f94fc4ee)
- 🙋‍♂️🔒 UserDetail - permission layout ui [`65b36f8`](https://git.odit.services/lfk/frontend/commit/65b36f8e695479809566213ebcb64d51a7c6a52e)
- 🌎 added locale based csv/xlsx header parsing [`b606037`](https://git.odit.services/lfk/frontend/commit/b606037890856c1115315bb61d8469ff66dc4289)
- AddUserModal - username/email validation [`ff14f02`](https://git.odit.services/lfk/frontend/commit/ff14f024afda77e6f5f9e95a8f34e50cc8509790)
- UserDetail - link to permission page [`aaab95d`](https://git.odit.services/lfk/frontend/commit/aaab95d41400629683d89ee16013b6b4328c582c)
- UserDetail - add permission layout [`d65b463`](https://git.odit.services/lfk/frontend/commit/d65b463547a5cb4fb6f6de0f95795a66ebe92e31)
- RunnerDetail - button text fixes [`5552055`](https://git.odit.services/lfk/frontend/commit/5552055b989313e8999e424b6ad26c4398e8c6fa)
- 🧹 RunnerDetail cleanup + i18n 🌎 [`a871651`](https://git.odit.services/lfk/frontend/commit/a87165148a3ccdb4c4e10f2c6545d5fea9e78b30)
- ↙ added default fallback parsing to ImportRunnerModal [`ecad1ea`](https://git.odit.services/lfk/frontend/commit/ecad1ea839dbcea396217cb18fe335694406cdb1)
- 🌎 i18n [`366804a`](https://git.odit.services/lfk/frontend/commit/366804aa29eb0231e40017d1e5c3419486070174)
- ✨ ImportRunnerModal - compatibility for multi-component access [`f8a5913`](https://git.odit.services/lfk/frontend/commit/f8a59133a2534efdc9e13104eb64157a9724e746)
- ✨ use ImportRunnerModal in OrgDetail + Orgs [`8aa1d94`](https://git.odit.services/lfk/frontend/commit/8aa1d94a1aa976cc6cb4f7d3cc798822861676ad)
- ✨ ImportRunnerModal - hide team when loading from TeamDetail [`819b02a`](https://git.odit.services/lfk/frontend/commit/819b02a204cd43baaaf85d82cf0209a38028e6ce)
- 🚀 ImportRunnerModal - working demo [`6b91b22`](https://git.odit.services/lfk/frontend/commit/6b91b227136580180d5300b2614a6148995c4640)
- 🌎 i18n [`ae8bc01`](https://git.odit.services/lfk/frontend/commit/ae8bc01d9bab994aad6407102f3a6ef0167d008b)
- 🐞 fixed merge conflict errors [`cbc1d53`](https://git.odit.services/lfk/frontend/commit/cbc1d53cc257cbf60172dd4356831e43671ec626)
- ✨ basic cancel event dispatch from ImportRunnerModal [`cde4ec1`](https://git.odit.services/lfk/frontend/commit/cde4ec13efef8423464ebe90a4f0212a3be6cf77)
- 🧹 AddUserModal cleanup [`44f07ca`](https://git.odit.services/lfk/frontend/commit/44f07ca231307ebdbe38b822408c3965077053ef)
- 🙋‍♂️ UserDetail - active/inactive user state edit [`86c54e0`](https://git.odit.services/lfk/frontend/commit/86c54e04a84917383d3726b324c416ca1bd74627)
- UserDetail - update permission badges on change save [`358865d`](https://git.odit.services/lfk/frontend/commit/358865dc6a8e5a084078696048df99a67564a9e1)
- i18n 🌍 [`415f340`](https://git.odit.services/lfk/frontend/commit/415f340a68078e86fcf28d24b069ca030e28fc82)
- 👀 UsersOverview - disable advanced search [`b34e3ae`](https://git.odit.services/lfk/frontend/commit/b34e3aeed0f026308288fb568542cc0ba587b576)
- 🙋‍♂️ UserDetail - disable profile picture edit [`ef9fc59`](https://git.odit.services/lfk/frontend/commit/ef9fc596f57d63c1bf9c26614d746bf37394f832)
- 🌎 i18n [`2d48691`](https://git.odit.services/lfk/frontend/commit/2d4869128d2af5e7fa5e4bcf1d6b687fca6761e9)
- 🌎 i18n in ImportRunnerModal headers [`e34c91b`](https://git.odit.services/lfk/frontend/commit/e34c91b2cc64221cd553e80974a14448e3afc340)
- 🌎 last i18n keys [`6c2d13b`](https://git.odit.services/lfk/frontend/commit/6c2d13bd1721918f1ef847563cfad61284c40487)
- Fixed User group update [`1a799dc`](https://git.odit.services/lfk/frontend/commit/1a799dc30a4f051305e5279bb3325e64ab08f323)
- 🌎 UserDetail - more i18n keys [`e750c37`](https://git.odit.services/lfk/frontend/commit/e750c374739104c6aaac1abace9e9632b40342da)
- ⏫ general dependency bump [`b30b673`](https://git.odit.services/lfk/frontend/commit/b30b6734a1f4cc384f06147dfc657f2a1a05b3c0)
- 💻 updated VSCode recommended extensions [`fc21427`](https://git.odit.services/lfk/frontend/commit/fc214276859189c6e9b6fdc91eb440cf4afda7c0)
- 🐞 improved version builder from template [`09fd73b`](https://git.odit.services/lfk/frontend/commit/09fd73b13086025a31b4c1363331230e1c91e593)
- ⏫ general dependency bump [`9a1f7a1`](https://git.odit.services/lfk/frontend/commit/9a1f7a13f8669b690fb3f9533df7fdfa2f1bc093)
- 🌎 added more i18n keys [`169ffc1`](https://git.odit.services/lfk/frontend/commit/169ffc1b0b76c74350060c97218643789e0ea4f7)
- 😦 added missing dependencies [`822759a`](https://git.odit.services/lfk/frontend/commit/822759a6884eab81f62d96bd7e6f038fa8bc1926)
- fixed runner permissions [`e0356fa`](https://git.odit.services/lfk/frontend/commit/e0356fa360b74f91b38dc09351930c8c0778f845)
- Added friles to ignore [`9fbc1ba`](https://git.odit.services/lfk/frontend/commit/9fbc1ba0315fc0a2b7f3d95339cb17fc74e57903)
- ✨ new UsersEmptyState [`959b32d`](https://git.odit.services/lfk/frontend/commit/959b32de1c6fc74bec585630b3025babc1a8edc4)
- 🐞 fix package release for index template compatibility [`24bec66`](https://git.odit.services/lfk/frontend/commit/24bec66390bfddd57c0cd1d35de94a79bdbf477c)
- new license file version [CI SKIP] [`3d30734`](https://git.odit.services/lfk/frontend/commit/3d30734dc2716a88d823fe7a7650f17d14a8f18d)
- new license file version [CI SKIP] [`3281239`](https://git.odit.services/lfk/frontend/commit/3281239ff5b4968fb1dc8547e7c16f0789ec2b8c)
- 🐞 alphabetically sort permission targets in UserDetail [`ffd88ff`](https://git.odit.services/lfk/frontend/commit/ffd88ffc666d9d3add43dfa4ec4b6e01ca7084b5)
- new license file version [CI SKIP] [`e91e197`](https://git.odit.services/lfk/frontend/commit/e91e197873e524b93b2cf79286c66c8b776fbae2)
- Deleted files to ignore [`6704c07`](https://git.odit.services/lfk/frontend/commit/6704c07db0a00b17327a4b693b3f74ed41a1a41d)
- new license file version [CI SKIP] [`c6504c2`](https://git.odit.services/lfk/frontend/commit/c6504c2eaf1d0c5fb14a4035ccbf347d851068d9)
#### [0.4.0](https://git.odit.services/lfk/frontend/compare/0.3.1...0.4.0)
> 17 January 2021
- Merge commit 'a284806d3cb769030a4e28d0403190b746f8fc61' into dev [`#37`](https://git.odit.services/lfk/frontend/issues/37)
- ✨ AddRunnerModal [`66ffd8e`](https://git.odit.services/lfk/frontend/commit/66ffd8e936010960766e7f9021319d549e1d3e6b)
- ✨ RunnersOverview [`66a07c6`](https://git.odit.services/lfk/frontend/commit/66a07c6a51674a92d5e8459f250de2ab5ff6d902)
- ⚡ improved dev scripts for speed starts [`383f828`](https://git.odit.services/lfk/frontend/commit/383f82807f4090bdd2c3dcdc695b75093b854031)
- 🚀RELEASE v0.4.0 [`7d104a1`](https://git.odit.services/lfk/frontend/commit/7d104a151422a7c8306ef4d9f0ab25d26f8eb78b)
- ✨ dynamic contact info in AddRunnerModal [`d4579a9`](https://git.odit.services/lfk/frontend/commit/d4579a9a410a27676e0ed0285bd124696153aae4)
- 🐞 fix cross-env logic for faster dev starts ⚡ [`2ce4199`](https://git.odit.services/lfk/frontend/commit/2ce41990bf9911db11eb556ce4c9aa3b3e5ca16c)
- 👩‍💻 developer configs/ recommendations for VSCode [`5e02502`](https://git.odit.services/lfk/frontend/commit/5e02502a5c2b8ebf798cbc21856b4425f8510041)
- ⚡ re-enable PWA functionality via serviceworker [`a284806`](https://git.odit.services/lfk/frontend/commit/a284806d3cb769030a4e28d0403190b746f8fc61)
- 🔨 cleaned up build process + Dockerfile [`7e10c1d`](https://git.odit.services/lfk/frontend/commit/7e10c1db659c21cd737b5d1e10bf3e61c4e0de94)
- 🔨 cleaned up build process + Dockerfile [`1179063`](https://git.odit.services/lfk/frontend/commit/11790638d68e8c43f91448bd0c35f910a3d9e446)
- ⚡ improved serviceworker + PWA logic [`0583cbe`](https://git.odit.services/lfk/frontend/commit/0583cbe2664f8832c5eaa7fb155b3e6deccb2ed3)
- apply new gitignore config [`2e6874c`](https://git.odit.services/lfk/frontend/commit/2e6874c822f2f8e9a8a7b74b4765631ba08f0255)
- ⚡ PWA optimizations [`dccf7c6`](https://git.odit.services/lfk/frontend/commit/dccf7c6c8de0da0f25ad77e20822fb4fbf68a61a)
- ⏫ general dependency bumps [`03125b3`](https://git.odit.services/lfk/frontend/commit/03125b3a2d0ad4b12cffdec98f27da14f3f45f77)
- 🐞 gitignore fix [`e49dca0`](https://git.odit.services/lfk/frontend/commit/e49dca02754aa57cbe464066f11505db4a9b5ca9)
- gitignore fix [`a523379`](https://git.odit.services/lfk/frontend/commit/a523379b3a5f7f3cffecca82c0c066167da046ca)
- fix package:dev script [`aa6348a`](https://git.odit.services/lfk/frontend/commit/aa6348a29a6e9ecb9789681b7195527c4eef19e4)
- new license file version [CI SKIP] [`382cc3d`](https://git.odit.services/lfk/frontend/commit/382cc3d844bf0af7c46492907f8a9a78fadc25d0)
- 🧹 gitignore changes in public/index.html & svelte.config.js [`dd74d9e`](https://git.odit.services/lfk/frontend/commit/dd74d9ee89b80c46ce3d3347a3c7cbe34373019c)
#### [0.3.1](https://git.odit.services/lfk/frontend/compare/0.3.0...0.3.1)
> 16 January 2021
- Merge pull request 'feature/16-org-management' (#35) from feature/16-org-management into dev [`#16`](https://git.odit.services/lfk/frontend/issues/16)
- 🏃‍♂️🏃‍♂️🏃‍♂️ basic UI components for team management [`d87b879`](https://git.odit.services/lfk/frontend/commit/d87b879cc3d6c771a8a9932409e39068e1b2acdb)
- ✨ TeamDetail with edit,delete [`ccf09f9`](https://git.odit.services/lfk/frontend/commit/ccf09f97d5fb476113f24a9559a48bccd75fd0a5)
@@ -11,14 +161,15 @@ All notable changes to this project will be documented in this file. Dates are d
- 🔒 ConfirmOrgDeletion in OrgDetail [`d890112`](https://git.odit.services/lfk/frontend/commit/d8901126d0cc91cabe3b94a30a83f36e6288126d)
- ✨ basic TeamsOverview [`597e9e1`](https://git.odit.services/lfk/frontend/commit/597e9e1ea9da7c73bdcb8ef1ae1a13dfa68ff5a3)
- ✨ UX - ConfirmOrgDeletion cancel event reflection in datatable [`84a9cf0`](https://git.odit.services/lfk/frontend/commit/84a9cf069a4aa0940eaacc87ea67e745deabe939)
- 🧹 Dashboard - drop header bar [`f1833f1`](https://git.odit.services/lfk/frontend/commit/f1833f13d57595c23abf29bce1a2795cbb05a116)
- 🤝 attribution/ credits for icons and illustrations [`eb0dd3f`](https://git.odit.services/lfk/frontend/commit/eb0dd3f781f739c6511588a8e153c14a39096025)
- ✨ added new empty states [`66e6cd8`](https://git.odit.services/lfk/frontend/commit/66e6cd80d39ef18a29fd8ac80fbac929bd0c4f8c)
- Merge pull request 'feature/14-team-management' (#36) from feature/14-team-management into dev [`4285168`](https://git.odit.services/lfk/frontend/commit/42851686caae69e6672f48cd7df77ee4c2e49092)
- 🐞 fix Dashboard sidebar responsiveness [`6a81e36`](https://git.odit.services/lfk/frontend/commit/6a81e369fa20f0bb2846365a45f96e91e95fe2e7)
- 🧹 Dashboard - drop header bar [`f1833f1`](https://git.odit.services/lfk/frontend/commit/f1833f13d57595c23abf29bce1a2795cbb05a116)
- ✨ AddTeamModal working [`9bb027e`](https://git.odit.services/lfk/frontend/commit/9bb027ec4c73483907d396180f739dc3a11b2404)
- 🧹 TeamDetail cleanup [`7654b79`](https://git.odit.services/lfk/frontend/commit/7654b795c756ca198bad77068823032714408535)
- 🚀RELEASE v0.3.1 [`64ade90`](https://git.odit.services/lfk/frontend/commit/64ade901ded75fa738c713446343a209eca89ce6)
- 🤝 attribution/ credits for icons and illustrations [`eb0dd3f`](https://git.odit.services/lfk/frontend/commit/eb0dd3f781f739c6511588a8e153c14a39096025)
- 🔒 re-enable confirmation in OrgOverview [`ce6002a`](https://git.odit.services/lfk/frontend/commit/ce6002a631dd3c140f3892c750d052e89c135653)
- ✨ added new empty states [`66e6cd8`](https://git.odit.services/lfk/frontend/commit/66e6cd80d39ef18a29fd8ac80fbac929bd0c4f8c)
- Merge pull request 'feature/14-team-management' (#36) from feature/14-team-management into dev [`4285168`](https://git.odit.services/lfk/frontend/commit/42851686caae69e6672f48cd7df77ee4c2e49092)
- 🧹 TeamsOverview - formatting [`6870a7f`](https://git.odit.services/lfk/frontend/commit/6870a7f9b1fce2f06182dafa502f6dc4bb818bd3)
- 🔒 ConfirmOrgDeletion in OrgOverview [`83f19a7`](https://git.odit.services/lfk/frontend/commit/83f19a7572255b5c095c68d688a963dbe3cf4a75)
- 🧹 Team cleanups [`07f2e65`](https://git.odit.services/lfk/frontend/commit/07f2e65fc722c0328ee5a8dc4d01fc89c906fa86)

View File

@@ -14,7 +14,7 @@
</head>
<body>
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.3.1-RELEASE_INFO</span>
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.6.0-RELEASE_INFO</span>
<noscript>You need to enable JavaScript to run this app.</noscript>
<script src="/env.js"></script>
<script defer type="module" src="/_dist_/index.js"></script>

View File

@@ -1,10 +1,10 @@
{
"name": "@odit/lfk-frontend",
"version": "0.3.1",
"version": "0.6.0",
"scripts": {
"i18n-order": "node order.js",
"dev:all": "yarn prebuild && snowpack dev",
"dev:fast": "cross-env NODE_ENV_ODIT=development_fast node template-copy.js && snowpack dev",
"dev": "cross-env NODE_ENV_ODIT=development_fast node template-copy.js && yarn build:sw && snowpack dev",
"build": "yarn prebuild && snowpack build",
"prebuild": "cross-env NODE_ENV_ODIT=production node template-copy.js && yarn build:sw",
"build:sw": "workbox generateSW workbox-config.js",
@@ -12,32 +12,36 @@
"licenses:export": "license-exporter --json -o public"
},
"dependencies": {
"@odit/lfk-client-js": "0.0.12",
"@odit/lfk-client-js": "0.4.5",
"csvtojson": "^2.0.10",
"filepond": "4.25.1",
"gridjs": "3.2.2",
"gridjs": "3.3.0",
"localforage": "1.9.0",
"lodash.isequal": "^4.5.0",
"marked": "^2.0.0",
"svelte-filepond": "0.0.1",
"svelte-focus-trap": "1.0.1",
"svelte-i18n": "3.3.0",
"tailwindcss": "2.0.2",
"tinro": "0.5.6",
"svelte-i18n": "3.3.2",
"svelte-select": "^3.16.1",
"tailwindcss": "2.0.3",
"tinro": "0.5.12",
"toastify-js": "1.9.3",
"validator": "13.5.2"
"validator": "13.5.2",
"xlsx": "^0.16.9"
},
"devDependencies": {
"@odit/license-exporter": "0.0.9",
"@snowpack/plugin-svelte": "3.5.1",
"@snowpack/plugin-svelte": "3.5.2",
"auto-changelog": "^2.2.1",
"autoprefixer": "10.2.1",
"autoprefixer": "10.2.4",
"cross-env": "^7.0.3",
"postcss": "8.2.4",
"postcss-load-config": "3.0.0",
"release-it": "^14.2.2",
"snowpack": "3.0.10",
"svelte": "3.31.2",
"svelte-preprocess": "4.6.1",
"workbox-cli": "6.0.2"
"postcss": "8.2.6",
"postcss-load-config": "3.0.1",
"release-it": "^14.4.0",
"snowpack": "3.0.11",
"svelte": "3.32.3",
"svelte-preprocess": "4.6.8",
"workbox-cli": "6.1.0"
},
"release-it": {
"git": {
@@ -53,7 +57,7 @@
"publish": false
},
"hooks": {
"after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js && git add public/index.html && node order.js && git add src/locales"
"after:bump": "npx auto-changelog --commit-limit false -p -u --hide-credit && git add CHANGELOG.md && node versionbuilder.js && git add index.template.html && node order.js && git add src/locales"
}
}
}

1
public/imprint_en.md Normal file
View File

@@ -0,0 +1 @@
Nostrud tempor dolor aute ea excepteur aute mollit elit eiusmod exercitation. Magna laborum pariatur adipisicing pariatur cupidatat exercitation duis aliquip pariatur sint exercitation deserunt labore. Consectetur id laboris dolore nostrud do velit ipsum. Eu laboris velit do commodo ad ea sint ex cillum. Cillum ipsum qui eiusmod laborum mollit sunt dolore incididunt. Cillum sunt culpa veniam voluptate et qui ut magna anim occaecat ut mollit dolor. Duis irure proident eu incididunt dolore sunt nisi aute dolore amet eu fugiat laboris quis.

View File

@@ -1 +0,0 @@
<!-- this is configured with dev scripts -->

File diff suppressed because one or more lines are too long

1
public/privacy_en.md Normal file
View File

@@ -0,0 +1 @@
Nostrud tempor dolor aute ea excepteur aute mollit elit eiusmod exercitation. Magna laborum pariatur adipisicing pariatur cupidatat exercitation duis aliquip pariatur sint exercitation deserunt labore. Consectetur id laboris dolore nostrud do velit ipsum. Eu laboris velit do commodo ad ea sint ex cillum. Cillum ipsum qui eiusmod laborum mollit sunt dolore incididunt. Cillum sunt culpa veniam voluptate et qui ut magna anim occaecat ut mollit dolor. Duis irure proident eu incididunt dolore sunt nisi aute dolore amet eu fugiat laboris quis.

View File

@@ -48,6 +48,11 @@
OpenAPI.BASE = config.baseurl;
import { register as registerSW } from "./swmodule";
import TeamDetail from "./components/TeamDetail.svelte";
import UserPermissions from "./components/UserPermissions.svelte";
import RunnerDetail from "./components/RunnerDetail.svelte";
import Imprint from "./components/Imprint.svelte";
import Privacy from "./components/Privacy.svelte";
import ResetPassword from "./components/ResetPassword.svelte";
store.init();
registerSW();
</script>
@@ -57,10 +62,22 @@
<Route path="/forgot_password">
<ForgotPassword />
</Route>
{:else if $router.path.includes('/reset')}
<Route path="/reset/:resetkey" let:params>
<ResetPassword {params} />
</Route>
{:else if $router.path === '/about'}
<Route path="/about">
<About />
</Route>
{:else if $router.path === '/imprint'}
<Route path="/imprint">
<Imprint />
</Route>
{:else if $router.path === '/privacy'}
<Route path="/privacy">
<Privacy />
</Route>
{:else if $store.isLoggedIn}
<Dashboard>
<Transition>
@@ -71,8 +88,13 @@
<Route path="/">
<Users />
</Route>
<Route path="/:userid" let:params>
<UserDetail {params} />
<Route path="/:userid/*" let:params>
<Route path="/">
<UserDetail {params} />
</Route>
<Route path="/permissions/">
<UserPermissions {params} />
</Route>
</Route>
</Route>
<Route path="/tracks/*">
@@ -81,8 +103,13 @@
</Route>
<Route path="/:trackid" let:params />
</Route>
<Route path="/runners">
<Runners />
<Route path="/runners/*">
<Route path="/">
<Runners />
</Route>
<Route path="/:runnerid" let:params>
<RunnerDetail {params} />
</Route>
</Route>
<Route path="/teams/*">
<Route path="/">

View File

@@ -2,7 +2,7 @@
import { _ } from "svelte-i18n";
import { clickOutside } from "./outsideclick";
import { focusTrap } from "svelte-focus-trap";
import { RunnerOrganisationService } from "@odit/lfk-client-js";
import { RunnerOrganizationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
export let modal_open;
export let current_organizations;
@@ -35,7 +35,7 @@
text: "Organization is being added...",
duration: -1,
}).showToast();
RunnerOrganisationService.runnerOrganisationControllerPost({
RunnerOrganizationService.runnerOrganizationControllerPost({
name,
address: undefined,
contact: undefined,

View File

@@ -0,0 +1,312 @@
<script>
import { _ } from "svelte-i18n";
import { clickOutside } from "./outsideclick";
import { focusTrap } from "svelte-focus-trap";
import {
RunnerService,
RunnerTeamService,
RunnerOrganizationService,
} from "@odit/lfk-client-js";
import isEmail from "validator/es/lib/isEmail";
import isMobilePhone from "validator/es/lib/isMobilePhone";
import Toastify from "toastify-js";
export let modal_open;
export let current_runners;
$: selected_team = undefined;
let firstname_input;
let lastname_input;
let middlename_input;
let phone_input;
let email_input;
let teams = [];
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
teams = val;
});
let orgs = [];
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
orgs = val;
});
function focus(el) {
el.focus();
}
$: middlename_input_value = "";
$: phone_input_value = "";
$: email_input_value = "";
$: lastname_input_value = "";
$: firstname_input_value = "";
$: processed_last_submit = true;
$: isPhoneValidOrEmpty =
isMobilePhone(
phone_input_value
.replaceAll("(", "")
.replaceAll(")", "")
.replaceAll("-", "")
.replaceAll(" ", "")
) || phone_input_value === "";
$: isEmailValidOrEmpty =
isEmail(email_input_value) || email_input_value === "";
$: isLastnameValid = lastname_input_value.trim().length !== 0;
$: isFirstnameValid = firstname_input_value.trim().length !== 0;
$: createbtnenabled =
isFirstnameValid &&
isLastnameValid &&
isEmailValidOrEmpty &&
isPhoneValidOrEmpty;
(() => {
document.onkeydown = (e) => {
e = e || window.event;
if (e.key === "Escape") {
modal_open = false;
}
if (e.keyCode === 13) {
if (createbtnenabled === true) {
createbtnenabled = false;
submit();
}
}
};
})();
function submit() {
if (processed_last_submit === true) {
processed_last_submit = false;
const toast = Toastify({
text: "Runner is being added...",
duration: -1,
}).showToast();
let postdata = {
group: selected_team,
firstname: firstname_input_value,
lastname: lastname_input_value,
};
if (middlename_input_value) {
postdata.middlename = middlename_input_value;
}
if (phone_input_value) {
postdata.phone = phone_input_value;
}
if (email_input_value) {
postdata.email = email_input_value;
}
RunnerService.runnerControllerPost(postdata)
.then((result) => {
firstname_input_value = "";
lastname_input_value = "";
middlename_input_value = "";
email_input_value = "";
modal_open = false;
//
Toastify({
text: "Runner added",
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_runners.push(result);
current_runners = current_runners;
})
.catch((err) => {
//
})
.finally(() => {
processed_last_submit = true;
//
toast.hideToast();
});
}
}
</script>
{#if modal_open}
<div
class="fixed z-10 inset-0 overflow-y-auto"
use:focusTrap
use:clickOutside
on:click_outside={() => {
modal_open = false;
}}>
<div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div
class="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<div
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<svg
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">
{$_('create-a-new-runner')}
</h3>
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_('please-provide-the-required-information-to-add-a-new-runner')}
</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>
<input
use:focus
autocomplete="off"
placeholder={$_('first-name')}
class:border-red-500={!isFirstnameValid}
class:focus:border-red-500={!isFirstnameValid}
class:focus:ring-red-500={!isFirstnameValid}
bind:value={firstname_input_value}
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" />
{#if !isFirstnameValid}
<span
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>
<input
autocomplete="off"
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" />
</div>
<div class="col-span-6">
<label
for="lastname"
class="block text-sm font-medium text-gray-700">Last Name</label>
<input
autocomplete="off"
placeholder="Last Name"
class:border-red-500={!isLastnameValid}
class:focus:border-red-500={!isLastnameValid}
class:focus:ring-red-500={!isLastnameValid}
bind:value={lastname_input_value}
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" />
{#if !isLastnameValid}
<span
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="team"
class="block text-sm font-medium text-gray-700">{$_('team')}</label>
<select
name="team"
bind:value={selected_team}
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">
{#each teams as team}
<option value={team.id}>
{team.parentGroup.name}
&gt;
{team.name}
</option>
{/each}
{#each orgs as org}
<option value={org.id}>{org.name}</option>
{/each}
</select>
</div>
<div class="col-span-6">
<label
for="phone"
class="block text-sm font-medium text-gray-700">Phone</label>
<input
autocomplete="off"
placeholder="Phone"
class:border-red-500={!isPhoneValidOrEmpty}
class:focus:border-red-500={!isPhoneValidOrEmpty}
class:focus:ring-red-500={!isPhoneValidOrEmpty}
bind:value={phone_input_value}
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" />
{#if !isPhoneValidOrEmpty}
<span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
{$_('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>
<input
autocomplete="off"
placeholder={$_('e-mail-adress')}
class:border-red-500={!isEmailValidOrEmpty}
class:focus:border-red-500={!isEmailValidOrEmpty}
class:focus:ring-red-500={!isEmailValidOrEmpty}
bind:value={email_input_value}
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" />
{#if !isEmailValidOrEmpty}
<span
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>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
disabled={!createbtnenabled}
class:opacity-50={!createbtnenabled}
on:click={submit}
type="button"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
{$_('create')}
</button>
<button
on:click={() => {
modal_open = false;
}}
type="button"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
{$_('cancel')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -3,7 +3,7 @@
import { clickOutside } from "./outsideclick";
import { focusTrap } from "svelte-focus-trap";
import {
RunnerOrganisationService,
RunnerOrganizationService,
RunnerTeamService,
} from "@odit/lfk-client-js";
import Toastify from "toastify-js";
@@ -33,7 +33,7 @@
})();
$: parentGroup = undefined;
$: orgs = [];
RunnerOrganisationService.runnerOrganisationControllerGetAll().then((val) => {
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
orgs = val;
});
function submit() {
@@ -147,7 +147,7 @@
class="block text-sm font-medium text-gray-700">{$_('organization')}</label>
<select
bind:value={parentGroup}
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 dark:bg-gray-900 dark:text-gray-100 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">
{#each orgs as t}
<option value={t.id}>{t.name}</option>
{/each}

View File

@@ -28,7 +28,7 @@
$: isLastnameValid = lastname_input_value.trim().length !== 0;
$: isFirstnameValid = firstname_input_value.trim().length !== 0;
$: createbtnenabled =
isFirstnameValid && isLastnameValid && isEmailValid && isPasswordValid;
isFirstnameValid && isLastnameValid && isPasswordValid && isEmailValid;
(function () {
document.onkeydown = function (e) {
e = e || window.event;
@@ -50,19 +50,14 @@
text: "User is being added...",
duration: -1,
}).showToast();
let postdata={
UserService.userControllerPost({
firstname: firstname_input_value,
lastname: lastname_input_value,
middlename: middlename_input_value,
password: password_input_value
};
if(email_input_value!==""){
postdata.email=email_input_value;
}
if(username_input_value!==""){
postdata.username=username_input_value;
}
UserService.userControllerPost(postdata)
password: password_input_value,
email: email_input_value,
username: username_input_value,
})
.then((result) => {
firstname_input_value = "";
lastname_input_value = "";
@@ -77,7 +72,7 @@
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
current_users.push(result);
current_users=current_users;
current_users = current_users;
})
.catch((err) => {
//
@@ -238,9 +233,6 @@
<input
autocomplete="off"
placeholder={$_('e-mail-adress')}
class:border-red-500={!isEmailValid}
class:focus:border-red-500={!isEmailValid}
class:focus:ring-red-500={!isEmailValid}
bind:value={email_input_value}
bind:this={email_input}
type="email"
@@ -248,9 +240,7 @@
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')}
</span>
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>

View File

@@ -30,7 +30,7 @@
<Table />
</div>
<div
class="widget w-full p-4 mb-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890">
class="widget w-full p-4 mb-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between mb-6">
<div class="flex flex-col">
<div class="text-sm font-light text-grey-500">Regular</div>

View File

@@ -2,7 +2,7 @@
import { _ } from "svelte-i18n";
import { clickOutside } from "./outsideclick";
import { focusTrap } from "svelte-focus-trap";
import { RunnerOrganisationService } from "@odit/lfk-client-js";
import { RunnerOrganizationService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import { createEventDispatcher } from "svelte";
export let modal_open;
@@ -13,7 +13,7 @@
dispatch("cancelDelete", { id: delete_org.id });
}
function deleteOrg() {
RunnerOrganisationService.runnerOrganisationControllerRemove(
RunnerOrganizationService.runnerOrganizationControllerRemove(
delete_org.id,
true
)

View File

@@ -1,4 +1,4 @@
<div class="w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890">
<div class="w-full p-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-between mb-6">
<div class="flex flex-col">
<div class="text-sm font-light text-grey-500">Conversions</div>
@@ -18,16 +18,16 @@
<div class="flex flex-col w-full">
<ul class="list-none">
<li><a
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890"
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100"
href="/">Today</a></li>
<li><a
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890"
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100"
href="/">This week</a></li>
<li><a
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890"
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100"
href="/">This month</a></li>
<li><a
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100 dark:bg-grey-895 dark:hover:bg-grey-890"
class="flex flex-row items-center justify-start h-10 w-full px-2 bg-white hover:bg-grey-100"
href="/">This year</a></li>
</ul>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
<script>
import { _ } from "svelte-i18n";
import localForage from "localforage";
import store from "../store";
import { router } from "tinro";
import NoComponentLoaded from "./NoComponentLoaded.svelte";
import { AuthService } from "@odit/lfk-client-js";
@@ -36,175 +37,91 @@
</svg>
<span>{$_('dashboard-title')}</span>
</a>
<a
class:bg-gray-100={$router.path.includes('/orgs/')}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/orgs/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-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="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" /></svg>
<span>{$_('orgs')}</span>
</a>
<a
class:bg-gray-100={$router.path === '/users/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/users/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:GET')}
<a
class:bg-gray-100={$router.path.includes('/orgs/')}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/orgs/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg>
<span>{$_('users')}</span>
</a>
<a
class:bg-gray-100={$router.path === '/runners/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/runners/">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
width="24"
height="24"><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>
<span>{$_('runners')}</span>
</a>
<a
class:bg-gray-100={$router.path === '/teams/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/teams/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"><path fill="none" d="M0 0h24v24H0z" />
<path
d="M17 19h2v-8h-6v8h2v-6h2v6zM3 19V4a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v5h2v10h1v2H2v-2h1zm4-8v2h2v-2H7zm0 4v2h2v-2H7zm0-8v2h2V7H7z" /></svg>
<span>{$_('orgs')}</span>
</a>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:GET')}
<a
class:bg-gray-100={$router.path === '/users/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/users/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg>
<span>{$_('teams')}</span>
</a>
<a
class:bg-gray-100={$router.path === '/tracks/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/tracks/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path
fill="currentColor"
d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg>
<span>{$_('users')}</span>
</a>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')}
<a
class:bg-gray-100={$router.path === '/runners/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/runners/">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z" /></svg>
<span>{$_('tracks')}</span>
</a>
<a
class:bg-gray-100={false}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="#">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor">
<path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z" />
<path
fill-rule="evenodd"
d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm9.707 5.707a1 1 0 00-1.414-1.414L9 12.586l-1.293-1.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd" />
</svg>
<span>Checklists</span>
</a>
<div>
<div
tabindex="0"
class:bg-gray-100={false}
class="flex items-center justify-between px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
role="button"
on:click={() => {
dropdown1 = !dropdown1;
}}>
<div class="flex items-center">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor">
<path
fill-rule="evenodd"
d="M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z"
clip-rule="evenodd" />
</svg>
<span>Integrations</span>
</div>
{#if dropdown1}
<svg
class="flex-shrink-0 w-4 h-4 ml-2 transition transform"
xmlns="http://www.w3.org/2000/svg"
style="transform:rotate(90deg)"
viewBox="0 0 20 20"
fill="currentColor">
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd" />
</svg>
{:else}
<svg
class="flex-shrink-0 w-4 h-4 ml-2 transition transform"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor">
<path
fill-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clip-rule="evenodd" />
</svg>
{/if}
</div>
{#if dropdown1}
<div class="mb-4">
<a
class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900"
href="#">Shopify</a>
<a
class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900"
href="#">Slack</a>
<a
class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900"
href="#">Zapier</a>
</div>
{/if}
</div>
<a
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="#">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor">
<path
fill-rule="evenodd"
d="M5 5a3 3 0 015-2.236A3 3 0 0114.83 6H16a2 2 0 110 4h-5V9a1 1 0 10-2 0v1H4a2 2 0 110-4h1.17C5.06 5.687 5 5.35 5 5zm4 1V5a1 1 0 10-1 1h1zm3 0a1 1 0 10-1-1v1h1z"
clip-rule="evenodd" />
<path d="M9 11H3v5a2 2 0 002 2h4v-7zM11 18h4a2 2 0 002-2v-5h-6v7z" />
</svg>
<span>{$_('changelog')}</span>
</a>
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>
<span>{$_('runners')}</span>
</a>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:GET')}
<a
class:bg-gray-100={$router.path === '/teams/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/teams/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path
fill="currentColor"
d="M96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm448 0c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm32 32h-64c-17.6 0-33.5 7.1-45.1 18.6 40.3 22.1 68.9 62 75.1 109.4h66c17.7 0 32-14.3 32-32v-32c0-35.3-28.7-64-64-64zm-256 0c61.9 0 112-50.1 112-112S381.9 32 320 32 208 82.1 208 144s50.1 112 112 112zm76.8 32h-8.3c-20.8 10-43.9 16-68.5 16s-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h288c26.5 0 48-21.5 48-48v-28.8c0-63.6-51.6-115.2-115.2-115.2zm-223.7-13.4C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg>
<span>{$_('teams')}</span>
</a>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('TRACK:GET')}
<a
class:bg-gray-100={$router.path === '/tracks/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
href="/tracks/">
<svg
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
fill="currentColor"
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path
fill="currentColor"
d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z" /></svg>
<span>{$_('tracks')}</span>
</a>
{/if}
<a
class:bg-gray-100={$router.path === '/settings/'}
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
@@ -258,6 +175,10 @@
</nav>
</nav>
<div class="ml-0 transition md:ml-60">
<header on:click={() => {
navOpen = true;
}} class="flex items-center justify-between w-full px-4 bg-white border-b h-14 md:hidden"><button class="block btn btn-light md:hidden">
<span class="sr-only">Menu</span><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentcolor"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4A1 1 0 013 5zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path></svg></button></header>
<slot>
<NoComponentLoaded />
</slot>

View File

@@ -1,12 +1,13 @@
<script>
import { onMount } from "svelte";
import { _ } from "svelte-i18n";
$: releaseinfo = "";
setTimeout(() => {
onMount(() => {
releaseinfo = document
.getElementById("buildinfo")
.textContent.replace("RELEASE_INFO-", "")
.replace("-RELEASE_INFO", "");
}, 1500);
});
const year = new Date().getFullYear();
</script>
@@ -31,5 +32,9 @@
target="_blank"
rel="noopener, noreferrer"
href="https://git.odit.services/lfk/frontend/src/tag/{releaseinfo}">{releaseinfo}</a>
-
<a class="underline" href="/privacy">{$_('privacy')}</a>
-
<a class="underline" href="/imprint">{$_('imprint')}</a>
</p>
</footer>

View File

@@ -0,0 +1,364 @@
<script>
import csv from "csvtojson";
import { read as readXlsx, utils as xlsx_utils } from "xlsx";
import { _ } from "svelte-i18n";
import { clickOutside } from "./outsideclick";
import { focusTrap } from "svelte-focus-trap";
import Toastify from "toastify-js";
import {
ImportService,
RunnerTeamService,
RunnerOrganizationService,
} from "@odit/lfk-client-js";
import { createEventDispatcher } from "svelte";
export let opened_from;
export let passed_org;
export let passed_orgs;
export let passed_team;
export let current_runners;
export let import_modal_open;
$: searchvalue = "";
const dispatch = createEventDispatcher();
function cancelModal() {
import_modal_open = false;
dispatch("cancel");
}
(() => {
document.onkeydown = (e) => {
e = e || window.event;
if (e.key === "Escape") {
import_modal_open = false;
}
if (e.keyCode === 13) {
//
}
};
})();
let orgs = [];
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
orgs = val;
});
let teams = [];
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
teams = val;
});
let selected_org;
$: selected_org_or_team = "";
let files;
let recent_processed = true;
$: json_output = [];
$: {
if (files) {
if (
files[0].type ===
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
) {
const reader = new FileReader();
reader.addEventListener("load", async (e) => {
const data = new Uint8Array(e.target.result);
const out = readXlsx(data, { type: "array" });
json_output = xlsx_utils.sheet_to_json(
out.Sheets[Object.keys(out.Sheets)[0]]
);
});
reader.readAsArrayBuffer(files[0]);
} else {
const reader = new FileReader();
reader.addEventListener("load", async (e) => {
json_output = await csv({
delimiter: [";", ","],
trim: true,
}).fromString(e.target.result);
});
reader.readAsText(files[0]);
}
}
}
function importAction() {
if (recent_processed === true) {
const toast = Toastify({
text: "Runners are being imported...",
duration: -1,
}).showToast();
recent_processed = false;
const mapped = json_output.map(function (runner) {
return {
firstname: runner[`${$_("csv_import__firstname")}`],
middlename: runner[`${$_("csv_import__middlename")}`],
lastname: runner[`${$_("csv_import__lastname")}`],
team:
runner[`${$_("csv_import__team")}`] ||
runner[`${$_("csv_import__class")}`],
};
});
let org = 0;
if (opened_from === "OrgDetail") {
org = passed_org.id;
}
if (opened_from === "OrgOverview") {
org = parseInt(selected_org);
}
if (opened_from === "OrgOverview" || opened_from === "OrgDetail") {
ImportService.importControllerPostOrgsJson(org, mapped)
.then((resp) => {
toast.hideToast();
recent_processed = true;
Toastify({
text: "Import finished",
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
cancelModal();
})
.catch((err) => {
toast.hideToast();
recent_processed = true;
});
}
if (opened_from === "TeamDetail") {
ImportService.importControllerPostTeamsJson(passed_team.id, mapped)
.then((resp) => {
toast.hideToast();
recent_processed = true;
Toastify({
text: "Import finished",
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
cancelModal();
})
.catch((err) => {
toast.hideToast();
recent_processed = true;
});
}
if (opened_from === "RunnerOverview") {
if (selected_org_or_team.includes("ORG_")) {
selected_org_or_team = selected_org_or_team.split("_")[1];
ImportService.importControllerPostOrgsJson(
selected_org_or_team,
mapped
)
.then((resp) => {
current_runners = current_runners.concat(resp);
toast.hideToast();
recent_processed = true;
Toastify({
text: "Import finished",
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
cancelModal();
})
.catch((err) => {
toast.hideToast();
recent_processed = true;
});
}
if (selected_org_or_team.includes("TEAM_")) {
selected_org_or_team = selected_org_or_team.split("_")[1];
ImportService.importControllerPostTeamsJson(
selected_org_or_team,
mapped
)
.then((resp) => {
current_runners = current_runners.concat(resp);
toast.hideToast();
recent_processed = true;
Toastify({
text: "Import finished",
duration: 500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
cancelModal();
})
.catch((err) => {
toast.hideToast();
recent_processed = true;
});
}
}
}
}
</script>
{#if import_modal_open}
<div
class="fixed z-10 inset-0 overflow-y-auto"
use:focusTrap
use:clickOutside
on:click_outside={() => {
import_modal_open = false;
}}>
<div
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
<div
class="absolute inset-0 bg-gray-500 opacity-75"
data-id="modal_backdrop" />
</div>
<span
class="hidden sm:inline-block sm:align-middle sm:h-screen"
aria-hidden="true">&#8203;</span>
<div
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
role="dialog"
aria-modal="true"
aria-labelledby="modal-headline">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="sm:flex sm:items-start">
<div
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
<svg
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-2 sm:text-left w-full">
<h3 class="text-lg leading-6 font-bold mt-2 text-gray-900">
{$_('runner-import')}
</h3>
</div>
</div>
<div class="mt-5 text-center sm:mt-0 sm:ml-2 sm:text-left w-full">
{#if json_output.length === 0}
<div class="mt-2 mb-6">
<p class="text-sm text-gray-500">
{$_('please-provide-the-required-csv-xlsx-file')}
</p>
</div>
<div class="overflow-hidden relative mt-4 mb-4">
<input
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
bind:files
type="file" />
</div>
{/if}
{#if json_output.length > 0}
{#if opened_from === 'OrgOverview'}
<p>{$_('import__target-organization')}</p>
<select
name="team"
bind:value={selected_org}
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">
{#each passed_orgs as o}
<option value={o.id}>{o.name}</option>
{/each}
</select>
<p>{$_('bitte-bestaetige-diese-laeufer-fuer-den-import')}</p>
{/if}
{#if opened_from === 'RunnerOverview'}
<p>Group</p>
<select
name="team"
bind:value={selected_org_or_team}
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">
{#each teams as team}
<option value="TEAM_{team.id}">
{team.parentGroup.name}
&gt;
{team.name}
</option>
{/each}
{#each orgs as org}
<option value="ORG_{org.id}">{org.name}</option>
{/each}
</select>
{/if}
{#if opened_from === 'OrgDetail'}
<p>
{$_('runnerimport_verify_runners_org', {
values: { org_name: passed_org.name },
})}
</p>
{/if}
<input
type="search"
bind:value={searchvalue}
placeholder={$_('datatable.search')}
aria-label={$_('datatable.search')}
class="p-2 w-full" />
<div class="relative w-full mt-4 mb-4">
<div class="w-full overflow-x-auto">
<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">
{$_('csv_import__firstname')}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('csv_import__middlename')}
</th>
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('csv_import__lastname')}
</th>
{#if (opened_from !== 'TeamDetail' && opened_from !== 'RunnerOverview') || (opened_from === 'RunnerOverview' && selected_org_or_team.includes('ORG_'))}
<th
scope="col"
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{$_('csv_import__team')}
</th>
{/if}
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
{#each json_output as runner}
{#if Object.values(runner)
.toString()
.toLowerCase()
.includes(searchvalue)}
<tr>
<td class="px-6 py-4 whitespace-nowrap">
{runner[`${$_('csv_import__firstname')}`]}
</td>
<td class="px-6 py-4 whitespace-nowrap">
{runner[`${$_('csv_import__middlename')}`] || ''}
</td>
<td class="px-6 py-4 whitespace-nowrap">
{runner[`${$_('csv_import__lastname')}`]}
</td>
{#if (opened_from !== 'TeamDetail' && opened_from !== 'RunnerOverview') || (opened_from === 'RunnerOverview' && selected_org_or_team.includes('ORG_'))}
<td class="px-6 py-4 whitespace-nowrap">
{runner[`${$_('csv_import__team')}`] || runner[`${$_('csv_import__class')}`] || '---'}
</td>
{/if}
</tr>
{/if}
{/each}
</tbody>
</table>
</div>
<button
on:click={importAction}
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')}
</button>
<button
on:click={() => {
json_output = [];
cancelModal();
}}
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">
{$_('cancel')}
</button>
</div>
{/if}
</div>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -0,0 +1,45 @@
<script>
import { _, getLocaleFromNavigator } from "svelte-i18n";
import * as css from "./simple.css";
import marked from "marked";
import Footer from "./Footer.svelte";
let html = "";
async function load() {
let md = await fetch("/imprint_" + getLocaleFromNavigator() + ".md");
if (!md.ok) {
md = await fetch("/imprint_en.md");
}
html = marked(await md.text());
}
const promise = load();
</script>
<div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12">
<div class="text-center mb-8">
<h1
class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl">
{$_('imprint')}
</h1>
</div>
</div>
<div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{#await promise}
<p class="text-center w-full">{$_('imprint-loading')}</p>
{:then}
<div class="simplecontent">
{@html html}
</div>
{:catch error}
<div
class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
<span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b>
{error}
</span>
</div>
{/await}
</div>
</div>
<Footer />

View File

@@ -1,97 +0,0 @@
<script>
import store from "../store.js";
store.init();
const login = () => {
store.login();
};
</script>
<div
class="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
<div class="max-w-md w-full space-y-8">
<div>
<img
class="mx-auto h-12 w-auto"
src="/lfk-logo.png"
alt="" />
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
Sign in to your account
</h2>
<p class="mt-2 text-center text-sm text-gray-600">
Or
<a href="#" class="font-medium text-indigo-600 hover:text-indigo-500">
start your 14-day free trial
</a>
</p>
</div>
<div>
<input type="hidden" name="remember" value="true" />
<div class="rounded-md shadow-sm -space-y-px">
<div>
<label for="email-address" class="sr-only">Email address</label>
<input
id="email-address"
name="email"
type="email"
autocomplete="email"
required
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Email address" />
</div>
<div>
<label for="password" class="sr-only">Password</label>
<input
id="password"
name="password"
type="password"
autocomplete="current-password"
required
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Password" />
</div>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center">
<input
id="remember_me"
name="remember_me"
type="checkbox"
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" />
<label for="remember_me" class="ml-2 block text-sm text-gray-900">
Remember me
</label>
</div>
<div class="text-sm">
<a href="#" class="font-medium text-indigo-600 hover:text-indigo-500">
Forgot your password?
</a>
</div>
</div>
<div>
<button
on:click="{login}"
type="submit"
class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
<span class="absolute left-0 inset-y-0 flex items-center pl-3">
<!-- Heroicon name: lock-closed -->
<svg
class="h-5 w-5 text-indigo-500 group-hover:text-indigo-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true">
<path
fill-rule="evenodd"
d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
clip-rule="evenodd" />
</svg>
</span>
Sign in
</button>
</div>
</div>
</div>
</div>

View File

@@ -2,7 +2,6 @@
import { _ } from "svelte-i18n";
import StatCards from "./StatCards.svelte";
import store from "../store";
import ComponentDump from "./ComponentDump.svelte";
let navOpen = false;
</script>
@@ -21,5 +20,4 @@
👋</span>
</h1>
<StatCards />
<ComponentDump />
</div>

View File

@@ -1,9 +1,10 @@
<script>
import { RunnerOrganisationService } from "@odit/lfk-client-js";
import { RunnerOrganizationService } from "@odit/lfk-client-js";
import { _ } from "svelte-i18n";
import Toastify from "toastify-js";
import store from "../store";
import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte";
import ImportRunnerModal from "./ImportRunnerModal.svelte";
import PromiseError from "./PromiseError.svelte";
$: delete_triggered = false;
$: save_enabled = !data_changed;
@@ -12,7 +13,7 @@
let original = {};
$: data_loaded = false;
$: data_changed = JSON.stringify(orgdata) === JSON.stringify(original);
const promise = RunnerOrganisationService.runnerOrganisationControllerGetOne(
const promise = RunnerOrganizationService.runnerOrganizationControllerGetOne(
params.orgid
).then((value) => {
data_loaded = true;
@@ -21,8 +22,8 @@
});
let modal_open = false;
let delete_org = {};
function deleteOrganisation() {
// RunnerOrganisationService.runnerOrganisationControllerRemove(
function deleteOrganization() {
// RunnerOrganizationService.runnerOrganizationControllerRemove(
// original.id,
// false
// )
@@ -45,7 +46,7 @@
text: "updating organization",
duration: 2500,
}).showToast();
RunnerOrganisationService.runnerOrganisationControllerPut(
RunnerOrganizationService.runnerOrganizationControllerPut(
original.id,
orgdata
)
@@ -64,18 +65,39 @@
} else {
}
}
export let import_modal_open = false;
</script>
<ImportRunnerModal
on:cancelDelete={(event) => {
import_modal_open = false;
}}
current_runners={[]}
passed_team={{}}
passed_orgs={[]}
passed_org={orgdata}
opened_from="OrgDetail"
bind:import_modal_open />
<ConfirmOrgDeletion bind:modal_open bind:delete_org />
{#if data_loaded}
<section class="container p-5">
<div class="mb-8 text-3xl font-extrabold leading-tight">
{original.name}
<span data-id="org_actions_${orgdata.id}">
{#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')}
</button>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:DELETE')}
{#if delete_triggered}
<button
on:click={deleteOrganisation}
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>
<button
on:click={() => {
@@ -181,7 +203,7 @@
type="text"
bind:value={orgdata.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 dark:bg-gray-900 dark:text-gray-100 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
@@ -193,7 +215,7 @@
type="text"
bind:value={orgdata.contact}
name="contact"
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 dark:bg-gray-900 dark:text-gray-100 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
@@ -205,7 +227,7 @@
type="text"
bind:value={orgdata.address}
name="address"
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 dark:bg-gray-900 dark:text-gray-100 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>
</section>
{:else}

View File

@@ -2,7 +2,7 @@
import { _ } from "svelte-i18n";
let modal_open = false;
let delete_org = {};
import { RunnerOrganisationService } from "@odit/lfk-client-js";
import { RunnerOrganizationService } from "@odit/lfk-client-js";
import store from "../store";
import OrgsEmptyState from "./OrgsEmptyState.svelte";
import Toastify from "toastify-js";
@@ -11,7 +11,7 @@
$: active_deletes = [];
export let current_organizations = [];
const promise = RunnerOrganisationService.runnerOrganisationControllerGetAll().then(
const promise = RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
(val) => {
current_organizations = val;
}
@@ -25,7 +25,7 @@
}}
bind:modal_open
bind:delete_org />
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANISATION:GET')}
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:GET')}
{#await promise}
<div
class="bg-teal-lightest border-t-4 border-teal rounded-b text-teal-darkest px-4 py-3 shadow-md my-2"
@@ -79,7 +79,7 @@
<div class="flex items-center">
<div class="ml-4">
<div
class="text-sm font-medium text-gray-900 dark:text-gray-100">
class="text-sm font-medium text-gray-900">
{o.name}
</div>
</div>
@@ -89,7 +89,7 @@
<div class="flex items-center">
<div class="ml-4">
<div
class="text-sm font-medium text-gray-900 dark:text-gray-100">
class="text-sm font-medium text-gray-900">
{#if o.address}
{JSON.stringify(o.address)}
{:else}no address specified{/if}
@@ -101,7 +101,7 @@
<div class="flex items-center">
<div class="ml-4">
<div
class="text-sm font-medium text-gray-900 dark:text-gray-100">
class="text-sm font-medium text-gray-900">
{#if o.contact}
{JSON.stringify(o.contact)}
{:else}no contact specified{/if}
@@ -121,7 +121,7 @@
Delete</button>
<button
on:click={() => {
RunnerOrganisationService.runnerOrganisationControllerRemove(o.id, false)
RunnerOrganizationService.runnerOrganizationControllerRemove(o.id, false)
.then((resp) => {
current_organizations = current_organizations.filter((obj) => obj.id !== o.id);
Toastify({
@@ -146,7 +146,7 @@
<a
href="./{o.id}"
class="text-indigo-600 hover:text-indigo-900">Edit</a>
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANISATION:DELETE')}
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:DELETE')}
<button
on:click={() => {
active_deletes[o.id] = true;

View File

@@ -4,14 +4,15 @@
import AddOrgModal from "./AddOrgModal.svelte";
export let modal_open = false;
import OrgOverview from "./OrgOverview.svelte";
console.log(store.state.jwtinfo.userdetails.permissions);
import ImportRunnerModal from "./ImportRunnerModal.svelte";
let current_organizations = [];
export let import_modal_open = false;
</script>
<section class="container p-5">
<span class="mb-1 text-3xl font-extrabold leading-tight">
{$_('organizations')}
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANISATION:CREATE')}
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:CREATE')}
<button
on:click={() => {
modal_open = true;
@@ -21,11 +22,31 @@
{$_('create-organization')}
</button>
{/if}
{#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')}
</button>
{/if}
</span>
<p class="mb-8 text-lg text-gray-500">manage runner organizations</p>
<OrgOverview bind:current_organizations />
</section>
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANISATION:CREATE')}
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:CREATE')}
<AddOrgModal bind:current_organizations bind:modal_open />
<ImportRunnerModal
on:cancelDelete={(event) => {
import_modal_open = false;
}}
passed_team={{}}
passed_org={{}}
passed_orgs={current_organizations}
opened_from="OrgOverview"
current_runners={[]}
bind:import_modal_open />
{/if}

View File

@@ -0,0 +1,45 @@
<script>
import { _, getLocaleFromNavigator } from "svelte-i18n";
import * as css from "./simple.css";
import marked from "marked";
import Footer from "./Footer.svelte";
let html = "";
async function load() {
let md = await fetch("/privacy_" + getLocaleFromNavigator() + ".md");
if (!md.ok) {
md = await fetch("/privacy_en.md");
}
html = marked(await md.text());
}
const promise = load();
</script>
<div class="pt-12 px-4 sm:px-6 lg:px-8 lg:pt-20 bg-gray-900 pb-12">
<div class="text-center mb-8">
<h1
class="mt-9 font-display text-4xl leading-none font-semibold text-white sm:text-5xl lg:text-6xl">
{$_('privacy')}
</h1>
</div>
</div>
<div class="pt-0 pb-16 overflow-hidden lg:pt-12 lg:py-24">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{#await promise}
<p class="text-center w-full">{$_('privacy-loading')}</p>
{:then}
<div class="simplecontent">
{@html html}
</div>
{:catch error}
<div
class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
<span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b>
{error}
</span>
</div>
{/await}
</div>
</div>
<Footer />

View File

@@ -8,7 +8,7 @@
</div>
</div>
<div
class="w-full p-4 mb-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890">
class="w-full p-4 mb-4 rounded-lg bg-white border border-grey-100">
<div class="flex flex-row items-center justify-start p-4">
<div class="flex-shrink-0 w-24">
<img

View File

@@ -0,0 +1,129 @@
<script>
import { AuthService } from "@odit/lfk-client-js";
import { _ } from "svelte-i18n";
import Toastify from "toastify-js";
import "toastify-js/src/toastify.css";
let state = "reset_in_progress";
let password = "";
export let params;
function set_new_password() {
if(password.trim() !== ""){
Toastify({
text: $_('password-reset-in-progress'),
duration: 3500,
}).showToast();
AuthService.authControllerResetPassword(atob(params.resetkey),{ password })
.then((resp) => {
Toastify({
text: $_('password-reset-successful'),
duration: 3500,
}).showToast();
state="reset_success";
})
.catch((err) => {
state="reset_error";
});
} else {
Toastify({
text: $_('please-provide-a-password'),
duration: 3500,
}).showToast();
}
}
</script>
{#if state==="reset_success"}
<div class="min-h-screen flex items-center justify-center bg-gray-100">
<div class="max-w-md w-full py-12 px-6">
<img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" />
<p class="mt-6 text-lg text-center font-bold text-gray-900">
{$_('application_name')}
</p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900 font-bold">
{$_('successful-password-reset')}
</p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900">
{$_('you-can-now-use-your-new-password-to-log-in-to-your-account')}
</p>
<div class="mt-6">
<div class="mt-6">
<a
href="/login/"
class="text-center relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm">
{$_('go-to-login')}
</a>
</div>
</div>
</div>
</div>
{:else if state==="reset_error"}
<div class="min-h-screen flex items-center justify-center bg-gray-100">
<div class="max-w-md w-full py-12 px-6">
<img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" />
<p class="mt-6 text-lg text-center font-bold text-gray-900">
{$_('application_name')}
</p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900 font-bold">
{$_('password-reset-failed')}
</p>
<p class="mt-2 mb-2 text-sm text-center text-gray-900">
{$_('please-request-a-new-reset-mail')}
</p>
<div class="mt-6">
<div class="mt-6">
<a
href="/forgot_password/"
class="text-center relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm">
{$_('request-a-new-reset-mail')}
</a>
</div>
</div>
</div>
</div>
{:else if state==="reset_in_progress"}
<div class="min-h-screen flex items-center justify-center bg-gray-100">
<div class="max-w-md w-full py-12 px-6">
<img style="height:10rem;" class="mx-auto" src="/lfk-logo.png" alt="" />
<p class="mt-6 text-lg text-center font-bold text-gray-900">
{$_('application_name')}
</p>
<p class="mt-2 mb-4 text-md text-center text-gray-900">
{$_('reset-password')}
</p>
<div>
<div class="rounded-md shadow-sm">
<div>
<input
aria-label={$_('new-password')}
name="password"
type="password"
required=""
class="border-gray-300 placeholder-gray-500 appearance-none rounded-md relative block w-full px-3 py-2 border text-gray-900 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 focus:z-10 sm:text-sm"
placeholder={$_('new-password')}
bind:value={password} />
</div>
</div>
<div class="mt-5">
<button
on:click={set_new_password}
type="submit"
class="relative block w-full py-2 px-3 border border-transparent rounded-md text-white font-semibold bg-gray-800 hover:bg-gray-700 focus:bg-gray-900 focus:outline-none focus:shadow-outline sm:text-sm">
<span class="absolute left-0 inset-y pl-3">
<svg
class="h-5 w-5 text-gray-500"
fill="currentColor"
viewBox="0 0 20 20">
<path
fill-rule="evenodd"
d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
clip-rule="evenodd" />
</svg>
</span>
{$_('reset-my-password')}
</button>
</div>
</div>
</div>
</div>
{/if}

View File

@@ -0,0 +1,264 @@
<script>
import { _ } from "svelte-i18n";
import lodashIsEqual from "lodash.isequal";
import store from "../store";
import {
RunnerService,
RunnerTeamService,
RunnerOrganizationService,
} from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import PromiseError from "./PromiseError.svelte";
import isEmail from "validator/es/lib/isEmail";
let data_loaded = false;
export let params;
const runner_promise = RunnerService.runnerControllerGetOne(params.runnerid);
$: delete_triggered = false;
$: original_data = {};
$: editable = {};
$: changes_performed = !lodashIsEqual(original_data, editable);
$: isEmailValid =
(editable.email || "") === "" ||
(editable.email && isEmail(editable.email || ""));
$: isFirstnameValid = editable.firstname !== "";
$: isLastnameValid = editable.lastname !== "";
$: save_enabled =
changes_performed && isFirstnameValid && isLastnameValid && isEmailValid;
runner_promise.then((data) => {
data_loaded = true;
original_data = Object.assign(original_data, data);
original_data.group = original_data.group.id;
editable = Object.assign(editable, original_data);
});
let orgs = [];
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
orgs = val;
});
let teams = [];
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
teams = val;
});
function submit() {
if (data_loaded === true && save_enabled) {
Toastify({
text: $_("updating-runner"),
duration: 2500,
}).showToast();
RunnerService.runnerControllerPut(original_data.id, editable)
.then((resp) => {
Object.assign(original_data, editable);
original_data = editable;
Object.assign(original_data, editable);
Toastify({
text: $_("runner-updated"),
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
})
.catch((err) => {});
} else {
}
}
function deleteRunner() {
RunnerService.runnerControllerRemove(original_data.id, true)
.then((resp) => {
location.replace("./");
})
.catch((err) => {});
}
</script>
{#await runner_promise}
{$_('loading-runners')}
{:then}
<section class="container p-5 select-none">
<div class="flex flex-row mb-4">
<div class="w-full">
<nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start">
<li class="flex items-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="flex-shrink-0 w-5 h-5 mr-2"
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>
</li>
<li class="flex items-center">
<a class="mr-2" href="./">{$_('runners')}</a><svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-3 w-3 mr-2 stroke-current"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"><line
x1="5"
y1="12"
x2="19"
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li>
<li class="flex items-center">
<span class="mr-2">{original_data.firstname}
{original_data.middlename || ''}
{original_data.lastname}</span>
</li>
</ol>
</nav>
</div>
</div>
<div class="mb-8 text-3xl font-extrabold leading-tight">
{original_data.firstname}
{original_data.middlename || ''}
{original_data.lastname}
<span data-id="runner_actions_${editable.id}">
{#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>
<button
on:click={() => {
delete_triggered = !delete_triggered;
}}
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button>
{/if}
{#if !delete_triggered}
<button
on:click={() => {
delete_triggered = true;
}}
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-runner')}</button>
{/if}
{/if}
{#if !delete_triggered}
<button
disabled={!save_enabled}
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>
{/if}
</span>
</div>
<!-- -->
<div class="text-sm w-full">
<label
for="firstname"
class="font-medium text-gray-700">{$_('first-name')}</label>
<input
autocomplete="off"
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" />
{#if !isFirstnameValid}
<span
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>
<input
autocomplete="off"
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" />
</div>
<div class="text-sm w-full">
<label
for="lastname"
class="font-medium text-gray-700">{$_('last-name')}</label>
<input
autocomplete="off"
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" />
{#if !isLastnameValid}
<span
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>
<input
autocomplete="off"
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" />
{#if !isEmailValid}
<span
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>
<input
autocomplete="off"
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" />
</div>
<div class="text-sm w-full">
<span class="font-medium text-gray-700">{$_('group')}</span>
<select
bind:value={editable.group}
name="team"
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">
{#each teams as team}
<option value={team.id}>
{team.parentGroup.name}
&gt;
{team.name}
</option>
{/each}
{#each orgs as org}
<option value={org.id}>{org.name}</option>
{/each}
</select>
</div>
<div class="text-sm w-full">
<span class="font-medium text-gray-700">{$_('distance')}</span>
<br />
<span class="text-gray-700">{original_data.distance} km</span>
</div>
</section>
{:catch error}
<PromiseError {error} />
{/await}

View File

@@ -1,12 +1,49 @@
<script>
import { _ } from "svelte-i18n";
import RunnersEmptyState from "./RunnersEmptyState.svelte";
import store from "../store";
import AddRunnerModal from "./AddRunnerModal.svelte";
import ImportRunnerModal from "./ImportRunnerModal.svelte";
import RunnersOverview from "./RunnersOverview.svelte";
$: current_runners = [];
export let modal_open = false;
export let import_modal_open = false;
</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')}
<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">
Läufer hinzufügen
</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">
Läufer importieren
</button>
{/if}
</span>
<p class="mb-8 text-lg text-gray-500">läuft bei ihnen</p>
<RunnersOverview bind:current_runners />
</section>
<RunnersEmptyState />
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:CREATE')}
<AddRunnerModal bind:current_runners bind:modal_open />
<ImportRunnerModal
on:cancelDelete={(event) => {
import_modal_open = false;
}}
passed_team={{}}
passed_orgs={[]}
passed_org={{}}
bind:current_runners
opened_from="RunnerOverview"
bind:import_modal_open />
{/if}

View File

@@ -0,0 +1,187 @@
<script>
import { _ } from "svelte-i18n";
import { RunnerService,RunnerTeamService,
RunnerOrganizationService, } from "@odit/lfk-client-js";
import store from "../store";
import RunnersEmptyState from "./RunnersEmptyState.svelte";
import Select from 'svelte-select';
$: searchvalue = "";
$: active_deletes = [];
export let current_runners = [];
const runners_promise = RunnerService.runnerControllerGetAll().then((val) => {
current_runners = val;
});
$: selectedFilter_teams = null;
$: selectedFilter = null;
$: filter__teams = selectedFilter_teams||[];
$: filter__orgs = selectedFilter||[];
$:filterGroupIDs=filter__teams.concat(filter__orgs).map(i=>i.value)
$: 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;
});
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
orgs = val;
});
function should_display_based_on_id(id) {
if(searchvalue.toString().slice(-1)==="*"){
return id.toString().startsWith(searchvalue.replace("*",""))
}
return id.toString()===searchvalue;
}
</script>
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')}
{#await runners_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">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-1">
<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}></Select>
</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">
{$_('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>
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
{#each current_runners as runner}
{#if runner.firstname.toLowerCase().includes(searchvalue.toLowerCase())||runner.middlename.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">
<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}
</td>
<td class="px-6 py-4 whitespace-nowrap">
{#if runner.group.responseType === 'RUNNERTEAM'}
<a
href="../teams/{runner.group.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{runner.group.name}</a>
{/if}
{#if runner.group.responseType === 'RUNNERORGANIZATION'}
<a
href="../orgs/{runner.group.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{runner.group.name}</a>
{/if}
</td>
<td class="px-6 py-4 whitespace-nowrap">{runner.distance}</td>
{#if active_deletes[runner.id] === true}
<td
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
on:click={() => {
active_deletes[runner.id] = false;
}}
tabindex="0"
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel
Delete</button>
<button
on:click={() => {
RunnerService.runnerControllerRemove(runner.id, true)
.then((resp) => {
current_runners = current_runners.filter((obj) => obj.id !== runner.id);
})
.catch((err) => {
// error deleting user
});
}}
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">Edit</a>
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:DELETE')}
<button
on:click={() => {
active_deletes[runner.id] = true;
}}
tabindex="0"
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">{$_('delete')}</button>
{/if}
</td>
{/if}
</tr>
{/if}
{/if}
{/each}
</tbody>
</table>
</div>
{/if}
{:catch error}
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
<span class="inline-block align-middle mr-8">
<b class="capitalize">{$_('general_promise_error')}</b>
{error}
</span>
</div>
{/await}
{/if}

View File

@@ -18,7 +18,7 @@
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 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white">
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">
@@ -39,7 +39,7 @@
</a>
<div class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white">
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">
@@ -64,7 +64,7 @@
</div>
<div class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white">
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">
@@ -83,7 +83,7 @@
</div>
<div class="w-full lg:w-1/4">
<div
class="widget w-full p-4 rounded-lg bg-white border border-grey-100 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white">
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">
@@ -106,7 +106,7 @@
</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 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white">
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">
@@ -135,7 +135,7 @@
</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 dark:bg-grey-895 dark:border-grey-890 dark:bg-gray-900 dark:text-white">
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">

View File

@@ -1,11 +1,12 @@
<script>
import {
RunnerOrganisationService,
RunnerOrganizationService,
RunnerTeamService,
} from "@odit/lfk-client-js";
import { _ } from "svelte-i18n";
import Toastify from "toastify-js";
import store from "../store";
import ImportRunnerModal from "./ImportRunnerModal.svelte";
import PromiseError from "./PromiseError.svelte";
import ConfirmTeamDeletion from "./ConfirmTeamDeletion.svelte";
export let params;
@@ -16,6 +17,7 @@
[],
false,
];
export let import_modal_open = false;
$: delete_triggered = false;
$: save_enabled = !data_changed;
$: data_loaded = false;
@@ -28,7 +30,7 @@
teamdata = Object.assign(teamdata, value);
original = Object.assign(original, value);
});
RunnerOrganisationService.runnerOrganisationControllerGetAll().then((val) => {
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
orgs = val;
});
function deleteTeam() {
@@ -71,12 +73,32 @@
}
</script>
<ImportRunnerModal
current_runners={[]}
on:cancelDelete={(event) => {
import_modal_open = false;
}}
passed_team={teamdata}
passed_orgs={[]}
passed_org={{}}
opened_from="TeamDetail"
bind:import_modal_open />
<ConfirmTeamDeletion bind:modal_open bind:delete_team />
{#if data_loaded}
<section class="container p-5">
<div class="mb-8 text-3xl font-extrabold leading-tight">
{original.name}
<span data-id="org_actions_${teamdata.id}">
{#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')}
</button>
{/if}
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:DELETE')}
{#if delete_triggered}
<button
@@ -188,7 +210,7 @@
type="text"
bind:value={teamdata.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 dark:bg-gray-900 dark:text-gray-100 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
@@ -200,14 +222,14 @@
type="text"
bind:value={teamdata.contact}
name="contact"
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 dark:bg-gray-900 dark:text-gray-100 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="org" class="font-medium text-gray-700">Parent Organization</label>
<select
name="org"
bind:value={teamdata.parentGroup}
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 dark:bg-gray-900 dark:text-gray-100 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">
{#each orgs as o}
<option value={o.id}>{o.name}</option>
{/each}

View File

@@ -80,8 +80,7 @@
<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 dark:text-gray-100">
<div class="text-sm font-medium text-gray-900">
{t.name}
</div>
</div>
@@ -90,10 +89,11 @@
<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 dark:text-gray-100">
<div class="text-sm font-medium text-gray-900">
{#if t.parentGroup}
{t.parentGroup.name}
<a
href="../orgs/{t.parentGroup.id}"
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{t.parentGroup.name}</a>
{:else}no organization specified{/if}
</div>
</div>
@@ -102,8 +102,7 @@
<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 dark:text-gray-100">
<div class="text-sm font-medium text-gray-900">
{#if t.contact}
{JSON.stringify(t.contact)}
{:else}no contact specified{/if}

View File

@@ -2,31 +2,64 @@
import { _ } from "svelte-i18n";
import lodashIsEqual from "lodash.isequal";
import store from "../store";
import {
UserService,
UserGroupService,
PermissionService,
} from "@odit/lfk-client-js";
import isEmail from "validator/es/lib/isEmail";
import { UserService, UserGroupService } from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import PromiseError from "./PromiseError.svelte";
export let params;
const user_promise = UserService.userControllerGetOne(params.userid);
let data_loaded = false;
let usergroups_array_original = [];
const colors = [
"#f3558e",
"#17b978",
"#3498db",
"#3f3b3b",
"#775ada",
"#7ed6df_#000000",
"#000000",
"#21e6c1_#000000",
"#c0392b",
"#d35400",
"#7f8c8d",
"#6ab04c",
"#4834d4",
"#ff1f5a",
"#eac100",
];
let matched_colors = [];
$: delete_triggered = false;
$: original_data = {};
$: editable_userdata = {};
$: allpermissions = [];
$: allgroups = [];
$: allgroups_ids = [];
$: usergroups_array_objects = [];
$: usergroups_array = [];
let usergroups_array_original = [];
$: search_permission = "";
user_promise.then((data) => {
let current_target = "";
let colorindex = -1;
// alphabetically sort permissions for color compatibility for target
data.permissions = data.permissions.sort();
data.permissions.forEach((p) => {
const target = p.split(":")[0];
if (current_target !== p.split(":")[0]) {
colorindex++;
current_target = p.split(":")[0];
}
let background = colors[colorindex];
let foreground = "#fff";
if (background.includes("_")) {
foreground = background.split("_")[1];
background = background.split("_")[0];
}
matched_colors[target] = [background, foreground];
});
//
data_loaded = true;
original_data = Object.assign(original_data, data);
editable_userdata = data;
data.groups.forEach((g) => {
usergroups_array=usergroups_array.concat([g.id]);
usergroups_array = usergroups_array.concat([g.id]);
});
usergroups_array_original = usergroups_array;
allgroups.forEach((g) => {
@@ -36,37 +69,29 @@
UserGroupService.userGroupControllerGetAll().then((data) => {
allgroups = data;
});
const permissions_promise = PermissionService.permissionControllerGetAll();
permissions_promise.then((data) => {
data.forEach((p) => {
allpermissions=allpermissions.concat([p.target + ":" + p.action])
});
});
$: changes_performed = !lodashIsEqual(original_data, editable_userdata);
$: groups_changed = JSON.stringify(usergroups_array)===JSON.stringify(usergroups_array_original);
$: save_enabled = changes_performed||!groups_changed;
$: groups_changed =
JSON.stringify(usergroups_array) ===
JSON.stringify(usergroups_array_original);
$: save_enabled =
(changes_performed || !groups_changed) && isEmail(editable_userdata.email);
function submit() {
if (
!lodashIsEqual(original_data.permissions, editable_userdata.permissions)
) {
// TODO: add+delete permissions
}
if (data_loaded === true && save_enabled) {
let tmp=[];
usergroups_array.forEach(g => {
const group=allgroups.find(obj=>obj.id===g);
tmp.push(group);
});
editable_userdata.groups=tmp;
editable_userdata.groups = usergroups_array;
Toastify({
text: $_("updating-user"),
duration: 2500,
}).showToast();
UserService.userControllerPut(original_data.id, editable_userdata)
.then((resp) => {
Object.assign(original_data, editable_userdata);
original_data = editable_userdata;
Object.assign(original_data, editable_userdata);
Object.assign(original_data, resp);
Object.assign(editable_userdata, resp);
original_data.permissions = resp.permissions;
usergroups_array = [];
resp.groups.forEach((g) => {
usergroups_array = usergroups_array.concat([g.id]);
});
usergroups_array_original = usergroups_array;
//
Toastify({
text: $_("user-updated"),
@@ -74,9 +99,7 @@
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
})
.catch((err) => {
});
} else {
.catch((err) => {});
}
}
function deleteUser() {
@@ -84,8 +107,7 @@
.then((resp) => {
location.replace("./");
})
.catch((err) => {
});
.catch((err) => {});
}
</script>
@@ -135,7 +157,7 @@
</nav>
</div>
</div>
<div class="mb-8 text-3xl font-extrabold leading-tight">
<div class="mb-8 text-3xl font-extrabold">
{original_data.firstname}
{original_data.middlename || ''}
{original_data.lastname}
@@ -170,31 +192,30 @@
{/if}
</span>
</div>
<div class="mt-2 flex items-center">
<div class="mt-3 text-sm w-full">
<p class="ml-1 font-medium text-gray-700">Profile Picture</p>
<img
alt={$_('profile-picture')}
class="inline-block h-20 w-20 rounded-full overflow-hidden bg-gray-100"
class="h-20 w-20 rounded-full overflow-hidden bg-gray-100"
src={editable_userdata.profilePic} />
<button
type="button"
class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Change</button>
</div>
<!-- -->
<div class="mt-3 text-sm w-full">
<input
id="enabled"
on:change={() => {
editable_userdata.enabled = !editable_userdata.enabled;
// TODO: this reactive set does not work?
}}
name="enabled"
type="checkbox"
checked={editable_userdata.enabled}
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
<label
for="enabled"
class="ml-1 font-medium text-gray-700">Active?</label>
<p class="text-gray-500">set the user active/ inactive</p>
<br />
<p class="text-gray-500">
<input
id="enabled"
on:change={() => {
editable_userdata.enabled = !editable_userdata.enabled;
}}
name="enabled"
type="checkbox"
checked={editable_userdata.enabled}
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
set the user active/ inactive
</p>
</div>
<div class="text-sm w-full">
<label
@@ -244,6 +265,10 @@
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 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" />
</div>
{#if !isEmail(editable_userdata.email)}
<span
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">{$_('valid-email-is-required')}</span>
{/if}
<div class="text-sm w-full">
<label
for="username"
@@ -272,61 +297,29 @@
{/each}
</select>
</div>
<div class="text-sm w-full">
<span class="font-medium">Permissions</span>
<div class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center">
<!-- -->
<div class="flex flex-wrap -mx-1 overflow-hidden">
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">
verfügbare
</div>
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">erteilte</div>
</div>
<!-- -->
<div class="flex flex-wrap -mx-1 overflow-hidden">
{#if allpermissions.length > 0}
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">
<div
class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center">
{#each allpermissions as p}
{#if !editable_userdata.permissions.includes(p)}
<p
class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input">
{p}
<button
on:click={() => {
editable_userdata.permissions.push(p);
editable_userdata.permissions = editable_userdata.permissions;
}}
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-200 text-base font-medium text-black hover:bg-green-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm">+</button>
</p>
{/if}
{/each}
</div>
</div>
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/2">
<div
class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center">
{#each allpermissions as p}
{#if editable_userdata.permissions.includes(p)}
<p
class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input">
{p}
<button
on:click={() => {
editable_userdata.permissions = editable_userdata.permissions.filter((obj) => obj !== p);
}}
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-300 text-base font-medium text-black hover:bg-red-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm">-</button>
</p>
{/if}
{/each}
</div>
</div>
{/if}
</div>
<div class="text-sm w-full mt-8">
<p class="font-medium mb-4">
{$_('permissions')}
<a
class="px-4 py-2 bg-gray-500 rounded-md text-white"
href="/users/{params.userid}/permissions/">{$_('edit-permissions')}</a>
</p>
<div class="w-full sm:my-px sm:px-px sm:w-1/2">
<input
autocomplete="off"
placeholder="Search for permission"
type="text"
bind:value={search_permission}
class="mt-4 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 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" />
</div>
{#each original_data.permissions as p}
{#if p.toLowerCase().includes(search_permission.toLowerCase())}
<span
style="background:{matched_colors[p.split(':')[0]][0]};color:{matched_colors[p.split(':')[0]][1]};"
class="mt-1 inline-flex items-center justify-center px-2 py-1 text-xs font-bold leading-none text-indigo-100 rounded">{p}</span>
<!-- -->
{/if}
{/each}
</div>
</section>
{:catch error}

View File

@@ -0,0 +1,241 @@
<script>
import { _ } from "svelte-i18n";
import {
UserService,
PermissionService,
CreatePermission,
} from "@odit/lfk-client-js";
import Toastify from "toastify-js";
import PromiseError from "./PromiseError.svelte";
export let params;
let [
grantedPermissions_initial,
grantedPermissions,
inheritedPermissions,
to_add,
to_delete,
allpermissions,
promises,
] = [[], [], [], [], [], [], []];
$: original_data = {};
$: save_enabled =
JSON.stringify(grantedPermissions) ===
JSON.stringify(grantedPermissions_initial);
const user_promise = UserService.userControllerGetOne(params.userid);
user_promise.then((data) => {
original_data = Object.assign(original_data, data);
});
function submit() {
Toastify({
text: "updating permissions...",
duration: 2500,
}).showToast();
to_delete.forEach((d) => {
promises = promises.concat([
PermissionService.permissionControllerRemove(d, true),
]);
});
to_add.forEach((a) => {
promises = promises.concat([
PermissionService.permissionControllerPost(a),
]);
});
Promise.all(promises).then((values) => {
promises = [];
to_delete.forEach((d) => {
to_delete = to_delete.filter((o) => o !== d);
});
to_add.forEach((a) => {
to_add = to_add.filter(
(o) => o.target + ":" + o.action !== a.target + ":" + a.action
);
});
Toastify({
text: "Permissions updated!",
duration: 2500,
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
}).showToast();
});
}
Object.values(CreatePermission.target).forEach((t) => {
Object.values(CreatePermission.action).forEach((a) => {
allpermissions = allpermissions.concat([{ target: t, action: a }]);
});
});
UserService.userControllerGetPermissions(params.userid).then((val) => {
val.inherited.forEach((p) => {
inheritedPermissions = inheritedPermissions.concat([p]);
});
val.directlyGranted.forEach((p) => {
grantedPermissions = grantedPermissions.concat([p]);
});
grantedPermissions_initial = grantedPermissions;
});
</script>
{#await user_promise}
<!-- -->
{:then user}
<section class="container p-5 select-none">
<div class="flex flex-row mb-4">
<div class="w-full">
<nav class="w-full flex">
<ol class="list-none flex flex-row items-center justify-start">
<li class="flex items-center">
<svg
class="flex-shrink-0 w-5 h-5 mr-2"
fill="currentColor"
width="24"
height="24"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"><path
fill="currentColor"
d="M610.5 341.3c2.6-14.1 2.6-28.5 0-42.6l25.8-14.9c3-1.7 4.3-5.2 3.3-8.5-6.7-21.6-18.2-41.2-33.2-57.4-2.3-2.5-6-3.1-9-1.4l-25.8 14.9c-10.9-9.3-23.4-16.5-36.9-21.3v-29.8c0-3.4-2.4-6.4-5.7-7.1-22.3-5-45-4.8-66.2 0-3.3.7-5.7 3.7-5.7 7.1v29.8c-13.5 4.8-26 12-36.9 21.3l-25.8-14.9c-2.9-1.7-6.7-1.1-9 1.4-15 16.2-26.5 35.8-33.2 57.4-1 3.3.4 6.8 3.3 8.5l25.8 14.9c-2.6 14.1-2.6 28.5 0 42.6l-25.8 14.9c-3 1.7-4.3 5.2-3.3 8.5 6.7 21.6 18.2 41.1 33.2 57.4 2.3 2.5 6 3.1 9 1.4l25.8-14.9c10.9 9.3 23.4 16.5 36.9 21.3v29.8c0 3.4 2.4 6.4 5.7 7.1 22.3 5 45 4.8 66.2 0 3.3-.7 5.7-3.7 5.7-7.1v-29.8c13.5-4.8 26-12 36.9-21.3l25.8 14.9c2.9 1.7 6.7 1.1 9-1.4 15-16.2 26.5-35.8 33.2-57.4 1-3.3-.4-6.8-3.3-8.5l-25.8-14.9zM496 368.5c-26.8 0-48.5-21.8-48.5-48.5s21.8-48.5 48.5-48.5 48.5 21.8 48.5 48.5-21.7 48.5-48.5 48.5zM96 224c35.3 0 64-28.7 64-64s-28.7-64-64-64-64 28.7-64 64 28.7 64 64 64zm224 32c1.9 0 3.7-.5 5.6-.6 8.3-21.7 20.5-42.1 36.3-59.2 7.4-8 17.9-12.6 28.9-12.6 6.9 0 13.7 1.8 19.6 5.3l7.9 4.6c.8-.5 1.6-.9 2.4-1.4 7-14.6 11.2-30.8 11.2-48 0-61.9-50.1-112-112-112S208 82.1 208 144c0 61.9 50.1 112 112 112zm105.2 194.5c-2.3-1.2-4.6-2.6-6.8-3.9-8.2 4.8-15.3 9.8-27.5 9.8-10.9 0-21.4-4.6-28.9-12.6-18.3-19.8-32.3-43.9-40.2-69.6-10.7-34.5 24.9-49.7 25.8-50.3-.1-2.6-.1-5.2 0-7.8l-7.9-4.6c-3.8-2.2-7-5-9.8-8.1-3.3.2-6.5.6-9.8.6-24.6 0-47.6-6-68.5-16h-8.3C179.6 288 128 339.6 128 403.2V432c0 26.5 21.5 48 48 48h255.4c-3.7-6-6.2-12.8-6.2-20.3v-9.2zM173.1 274.6C161.5 263.1 145.6 256 128 256H64c-35.3 0-64 28.7-64 64v32c0 17.7 14.3 32 32 32h65.9c6.3-47.4 34.9-87.3 75.2-109.4z" /></svg>
</li>
<li class="flex items-center">
<a class="mr-2" href="./">{$_('users')}</a><svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-3 w-3 mr-2 stroke-current"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"><line
x1="5"
y1="12"
x2="19"
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li>
<li class="flex items-center">
<span class="mr-2"><a href="../">{original_data.firstname}
{original_data.middlename || ''}
{original_data.lastname}</a></span>
</li>
<li class="flex items-center">
<svg
stroke="currentColor"
fill="none"
stroke-width="2"
viewBox="0 0 24 24"
stroke-linecap="round"
stroke-linejoin="round"
class="h-3 w-3 mr-2 stroke-current"
height="1em"
width="1em"
xmlns="http://www.w3.org/2000/svg"><line
x1="5"
y1="12"
x2="19"
y2="12" />
<polyline points="12 5 19 12 12 19" /></svg>
</li>
<li class="flex items-center">
<span class="mr-2">Permissions</span>
</li>
</ol>
</nav>
</div>
</div>
<div class="mb-8 text-3xl font-extrabold">
Permissions:
{original_data.firstname}
{original_data.middlename || ''}
{original_data.lastname}
<span>
{#if promises.length === 0}
<button
disabled={save_enabled}
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>
{:else}
<button
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-yellow-600 text-base font-medium text-white hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-500 sm:ml-3 sm:w-auto sm:text-sm">Applying
Changes</button>
{/if}
</span>
</div>
<!-- -->
<div class="flex flex-wrap -mx-1 overflow-hidden">
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">verfügbare</div>
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">erteilte</div>
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">geerbte</div>
</div>
<!-- -->
<div class="flex flex-wrap -mx-1 overflow-hidden">
{#if allpermissions.length > 0}
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">
<div
class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center">
{#each allpermissions as p}
{#if !grantedPermissions.includes(p)}
<p
class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input">
{p.target + ':' + p.action}
<button
on:click={() => {
grantedPermissions = grantedPermissions.concat([p]);
if (to_delete.some((o) => o === p.id)) {
to_delete = to_delete.filter((o) => o !== p.id);
} else {
to_add = to_add.concat([
{
action: p.action,
target: p.target,
principal: original_data.id,
},
]);
}
}}
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-200 text-base font-medium text-black hover:bg-green-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 sm:ml-3 sm:w-auto sm:text-sm">+</button>
</p>
{/if}
{/each}
</div>
</div>
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">
<div
class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center">
{#each grantedPermissions as p}
<p
class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input">
{p.target + ':' + p.action}
<button
on:click={() => {
grantedPermissions = grantedPermissions.filter((o) => o.target + ':' + o.action !== p.target + ':' + p.action);
if (to_add.some((o) => o.target + ':' + o.action === p.target + ':' + p.action)) {
to_add = to_add.filter((o) => o.target + ':' + o.action !== p.target + ':' + p.action);
} else {
to_delete = to_delete.concat([p.id]);
}
}}
type="button"
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-300 text-base font-medium text-black hover:bg-red-700 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm">-</button>
</p>
{/each}
</div>
</div>
<div class="my-1 px-1 w-full overflow-hidden sm:w-1/3">
<div
class="border-4 border-dashed rounded mb-4 p-5 text-lg text-center">
{#each inheritedPermissions as p}
<p
class="block w-full mt-1 text-sm dark:border-gray-600 dark:bg-gray-700 bg-gray-200 p-2 focus:border-purple-400 focus:outline-none focus:shadow-outline-purple dark:text-gray-300 dark:focus:shadow-outline-gray form-input">
{p.target + ':' + p.action}
</p>
{/each}
</div>
</div>
{/if}
</div>
</section>
{:catch error}
<PromiseError {error} />
{/await}

View File

@@ -1,11 +1,13 @@
<script>
import { _ } from "svelte-i18n";
import AddUserModal from "./AddUserModal.svelte";
import users_empty from "./users_empty.svg";
let modal_open = false;
</script>
<div class="text-center items-center justify-center">
<p class="mb-16 text-lg text-gray-500">
<img class="w-full h-44" src={users_empty} alt="" />
<span class="font-bold">There are no users added yet.</span><br />
<span>Add your first user</span>
</p>

View File

@@ -29,17 +29,17 @@
{#if current_users.length === 0}
<UsersEmptyState />
{:else}
{#if advanced_search}
<!-- {#if advanced_search}
advanced search
{:else}
{:else} -->
<input
type="search"
bind:value={searchvalue}
placeholder={$_('datatable.search')}
aria-label={$_('datatable.search')}
class="gridjs-input gridjs-search-input mb-4" />
{/if}
<button
<!-- {/if} -->
<!-- <button
on:click={() => {
advanced_search = !advanced_search;
}}
@@ -48,7 +48,7 @@
{#if advanced_search}
toggle simple search
{:else}toggle advanced search{/if}
</button>
</button> -->
<div
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
<table class="divide-y divide-gray-200 w-full">
@@ -93,7 +93,7 @@
{/if}
<div class="ml-4">
<div
class="text-sm font-medium text-gray-900 dark:text-gray-100">
class="text-sm font-medium text-gray-900">
{u.firstname}
{u.middlename || ''}
{u.lastname}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -2,10 +2,17 @@
"404message": "Die gesuchte Seite wurde leider nicht gefunden.",
"404title": "Fehler 404",
"about": "Über",
"application_name": "Lauf für Kaya! \n- Admin",
"application_name": "Lauf für Kaya! - Admin",
"bitte-bestaetige-diese-laeufer-fuer-den-import": "Bitte die Läufer für den import bestätigen.",
"by": "von",
"cancel": "Abbrechen",
"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.",
"credits": "",
"csv_import__class": "Klasse",
"csv_import__firstname": "Vorname",
"csv_import__lastname": "Nachname",
"csv_import__middlename": "Mittelname",
"csv_import__team": "Team",
"dashboard-greeting": "Moin",
"datatable": {
"search": "🔍 Suche ...",
@@ -27,6 +34,8 @@
"goback": "Zur Startseite",
"hallo": "hallo",
"icon-image-credits": "Wir möchten uns außerdem für die verwendeten Icons und Bilder bedanken bei:",
"import-runners": "Läufer importieren",
"import__target-organization": "Ziel Organisation",
"invalid-mail-reset": "Das ist keine gültige E-Mail",
"lfk-is-os": "Das \"Lauf für Kaya!\" Frontend ist (wie alle anderen Projekte für den \"LfK!\" auch) ein OpenSource Projekt.",
"log_in": "Anmelden",
@@ -34,8 +43,11 @@
"login_is_checked": "Login wird überprüft",
"oss_credit_description": "Wir verwenden eine Menge Open Source-Software bei diesen Projekten und möchten uns bei den folgenden Projekten und Mitwirkenden bedanken, die dazu beitragen, Open Source großartig zu machen!",
"password": "Passwort",
"please-provide-the-required-csv-xlsx-file": "Bitte eine CSV oder XLSX Datei hochladen.",
"register": "Registrieren",
"reset-my-password": "Passwort zurücksetzen",
"runner-import": "Läufer Import",
"runnerimport_verify_runners_org": "Bitte die Läufer für den Import in die Organisation \"{org_name}\" bestätigen",
"runners": "Läufer",
"send-a-mail-to-lfk-odit-services": "Sende eine Mail an lfk@odit.services",
"settings": "Einstellungen",

View File

@@ -7,6 +7,7 @@
"address": "Address",
"application_name": "Lauf für Kaya! - Admin",
"author": "Author",
"bitte-bestaetige-diese-laeufer-fuer-den-import": "Please confirm these runners for import",
"browse": "Browse",
"by": "by",
"cancel": "Cancel",
@@ -14,14 +15,22 @@
"changelog": "Changelog",
"close": "Close",
"confirm-delete": "Confirm Delete",
"confirm-deletion": "Confirm Deletion",
"contact": "Contact",
"contact-information": "Contact Information",
"count_organizations": "# Organizations",
"count_teams": "# Teams",
"create": "Create",
"create-a-new-runner": "Create a new Runner",
"create-a-new-track": "Create a new Track",
"create-organization": "Create Organization",
"create-user": "Create User",
"credits": "Credits",
"csv_import__class": "Class",
"csv_import__firstname": "Firstname",
"csv_import__lastname": "Lastname",
"csv_import__middlename": "Middlename",
"csv_import__team": "Team",
"dashboard-greeting": "hello there",
"dashboard-title": "Dashboard",
"datatable": {
@@ -39,14 +48,19 @@
"no_matching_records_found": "No matching records found",
"an_error_happened_while_fetching_the_data": "An error happened while fetching the data"
},
"delete": "Delete",
"delete-organization": "Delete Organization",
"delete-runner": "Delete Runner",
"delete-team": "Delete Team",
"delete-user": "Delete User",
"dependency_name": "Name",
"distance": "Distance",
"distance-in-km": "Distance in km",
"dont-have-your-email-connected": "Don't have your email connected?",
"dont-panic-were-resetting-it": "Don't panic, we're resetting it ✌",
"drag-and-drop-your-files-or": "Drag & Drop your files or",
"e-mail-adress": "E-Mail Adress",
"edit-permissions": "edit permissions",
"email_address_or_username": "Email / username",
"error_on_login": "Error on login",
"faq": "FAQ",
@@ -75,10 +89,16 @@
"forgot_password?": "Forgot your password?",
"general-stats": "General Stats",
"general_promise_error": "😢 Error",
"go-to-login": "Go To Login",
"goback": "Go Home",
"group": "Group",
"groups": "Groups",
"hallo": "hello",
"icon-image-credits": "We also want to thank these projects for illustrations and icons:",
"import-runners": "Import runners",
"import__target-organization": "Target Organization",
"imprint": "Imprint 🧾",
"imprint-loading": "Imprint loading...",
"installed-version": "Installed version",
"invalid-mail-reset": "the provided email is invalid",
"last-name": "Last name",
@@ -86,6 +106,7 @@
"lfk-is-os": "The \"Lauf für Kaya!\" Frontend is (like all other projects for the \"LfK!\" Also) an open source project.",
"license": "License",
"licenses-are-being-loaded": "Licenses are being loaded...",
"loading-runners": "loading runners...",
"log_in": "Log in",
"log_in_to_your_account": "Log in to your account",
"login_is_checked": "Login is being checked...",
@@ -94,6 +115,8 @@
"manage-admin-users": "manage admin users",
"middle-name": "Middle name",
"minimum-lap-time-in-s": "minimum lap time in s",
"name": "Name",
"new-password": "New password",
"no-license-text-could-be-found": "No license text could be found 😢",
"no-tracks-added-yet": "there are no tracks added yet.",
"organization": "Organization",
@@ -102,20 +125,39 @@
"oss_credit_description": "We use a lot of open source software on these projects, and would like to thank the following projects and contributors who help make open source great!",
"password": "Password",
"password-is-required": "Password is required",
"password-reset-failed": "Password reset failed!",
"password-reset-in-progress": "Password Reset in Progress...",
"password-reset-successful": "Password Reset successful!",
"permissions": "Permissions",
"phone": "Phone",
"please-provide-a-password": "Please provide a password...",
"please-provide-the-required-csv-xlsx-file": "Please provide the required csv/ xlsx file",
"please-provide-the-required-information-to-add-a-new-runner": "Please provide the required information to add a new runner.",
"please-provide-the-required-information-to-add-a-new-track": "Please provide the required information to add a new track.",
"please-request-a-new-reset-mail": "Please request a new reset mail...",
"privacy": "Privacy 🔒",
"privacy-loading": "Privacy loading...",
"profile-picture": "Profile Picture",
"read-license": "Read License",
"register": "Register",
"repo_link": "Link",
"request-a-new-reset-mail": "Request a new reset mail",
"reset-my-password": "Reset my password",
"reset-password": "Reset your password",
"runner-import": "Runner Import",
"runner-updated": "Runner updated!",
"runnerimport_verify_runners_org": "Please confirm these runners for import into the organization \"{org_name}\"",
"runners": "Runners",
"save-changes": "Save Changes",
"send-a-mail-to-lfk-odit-services": "send a mail to lfk@odit.services",
"settings": "Settings",
"signout": "Sign out",
"stats-are-being-loaded": "stats are being loaded...",
"successful-password-reset": "Successful password reset!",
"team": "Team",
"team-name": "Team name",
"teams": "Teams",
"the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number": "the provided phone number is invalid.<br />please enter a valid international number...",
"this-might-take-a-moment": "This might take a moment 👀",
"total-distance": "total distance",
"total-donations": "total donations",
@@ -126,11 +168,13 @@
"track-length-in-m": "Track Length in m",
"track-name": "Track name",
"tracks": "Tracks",
"updating-runner": "Updating runner...",
"updating-user": "updating user...",
"user-updated": "User updated",
"username": "Username",
"users": "Users",
"valid-email-is-required": "valid email is required",
"welcome_wavinghand": "Welcome 👋",
"you-can-now-use-your-new-password-to-log-in-to-your-account": "You can now use your new password to log in to your account! 🎉",
"your_profile": "Your Profile"
}

View File

@@ -1 +0,0 @@
// this is configured with dev scripts

View File

@@ -1,5 +1,5 @@
const fs = require('fs');
const package = JSON.parse(fs.readFileSync(`./package.json`, { encoding: 'utf-8' }));
const original = fs.readFileSync(`./public/index.html`, { encoding: 'utf-8' });
const original = fs.readFileSync(`./index.template.html`, { encoding: 'utf-8' });
let out = original.replace(/RELEASE_INFO-(\S)+-RELEASE_INFO/gi, 'RELEASE_INFO-' + package.version + '-RELEASE_INFO');
fs.writeFileSync(`./public/index.html`, out);
fs.writeFileSync(`./index.template.html`, out);