Compare commits
232 Commits
Author | SHA1 | Date | |
---|---|---|---|
087c85e586 | |||
43df188df1 | |||
1d40c6d068 | |||
6614242df6 | |||
b25dc623cf | |||
9d3bb4e4e3 | |||
5dc11c285d | |||
575b4ce970 | |||
e23821a7cb | |||
e415258787 | |||
1451991d03 | |||
92fee08dc4 | |||
7b7e484091 | |||
e7291a31f3 | |||
2dc31912cc | |||
428a8a10ff | |||
8b2f1965e2 | |||
b18a99e4df | |||
1fac0c8640 | |||
f0be73c2cd | |||
42bd632539 | |||
f8014c6213 | |||
65f49489ad | |||
e9e24d5f2d | |||
369b09972c | |||
e4e2bdac72 | |||
ec0db39184 | |||
a02be78df5 | |||
3490abe9a7 | |||
b5013426e6 | |||
7dd0881d29 | |||
acf0562851 | |||
80c3a90d6f | |||
c6985087a8 | |||
ef50e2d5ce | |||
ed1dc6e8d5 | |||
c2d7a319a0 | |||
0e04298b7c | |||
3a5a73b02c | |||
901c7c1241 | |||
4e82369b16 | |||
945963db32 | |||
5741cbe756 | |||
6401aeb3a8 | |||
6a0c129d39 | |||
bbec9ffcdf | |||
541f1fa2e3 | |||
7897820632 | |||
0dd9de2abc | |||
7131ba99b6 | |||
c69026aa5b | |||
16ac96c64e | |||
1bb79b1c98 | |||
eeee272f03 | |||
2563e9d1d6 | |||
24bec66390 | |||
3d30734dc2 | |||
cbc1d53cc2 | |||
9a1f7a13f8 | |||
d48b9b7f91 | |||
358865dc6a | |||
e750c37473 | |||
71a589c10c | |||
3281239ff5 | |||
3e2cdddd5a | |||
1d4c3e51c7 | |||
1694c71528 | |||
169ffc1b0b | |||
89eb3259d4 | |||
ffd88ffc66 | |||
6c2d13bd17 | |||
8d92a75ef0 | |||
acb86ae266 | |||
473cf978b4 | |||
4debab2636 | |||
e91e197873 | |||
b30b6734a1 | |||
dadb80608a | |||
16c9969fb9 | |||
dc1644ed25 | |||
2cc9b3e1ed | |||
415f340a68 | |||
2d4869128d | |||
ae8bc01d9b | |||
e2552d9442 | |||
5d1b5d80b6 | |||
366804aa29 | |||
9240e0c903 | |||
7baaf2cff3 | |||
9fbc1ba031 | |||
6704c07db0 | |||
a87165148a | |||
ec4bcd093b | |||
5552055b98 | |||
03aa67034d | |||
fc21427685 | |||
819b02a204 | |||
de0bd5fd57 | |||
8aa1d94a1a | |||
f8a59133a2 | |||
c382f770dc | |||
cde4ec13ef | |||
ecad1ea839 | |||
6b91b22713 | |||
e34c91b2cc | |||
822759a688 | |||
b606037890 | |||
74d9b94119 | |||
b1e9f955b0 | |||
e0356fa360 | |||
fbc67eeb98 | |||
09fd73b130 | |||
259a76f46b | |||
c6504c2eaf | |||
7d104a1514 | |||
b3bd61c89e | |||
e49dca0275 | |||
03125b3a2d | |||
a523379b3a | |||
aa6348a29a | |||
b9f0f1a69a | |||
a284806d3c | |||
7e10c1db65 | |||
11790638d6 | |||
0583cbe266 | |||
2e6874c822 | |||
2ce41990bf | |||
c8aeba38ba | |||
5e02502a5c | |||
382cc3d844 | |||
dd74d9ee89 | |||
383f82807f | |||
d4579a9a41 | |||
66a07c6a51 | |||
66ffd8e936 | |||
44f07ca231 | |||
ff14f024af | |||
dccf7c6c8d | |||
10d7955f99 | |||
64ade901de | |||
eb0dd3f781 | |||
66e6cd80d3 | |||
959b32de1c | |||
9c6dc5b424 | |||
aaab95d414 | |||
7d4e93912c | |||
d65b463547 | |||
6e5a4bcb39 | |||
1a799dc30a | |||
e3d2676858 | |||
42851686ca | |||
e3943d868a | |||
7654b795c7 | |||
489244f1a9 | |||
cbcce336d6 | |||
52a96b2a4f | |||
ce6002a631 | |||
84a9cf069a | |||
83f19a7572 | |||
a1a4c8b56d | |||
d8901126d0 | |||
854db4ece8 | |||
07f2e65fc7 | |||
ccf09f97d5 | |||
8f9a4ebc04 | |||
f1833f13d5 | |||
6a81e369fa | |||
597e9e1ea9 | |||
9bb027ec4c | |||
fbbbaa5d49 | |||
aaec5a3fc9 | |||
7cd24cd51d | |||
c81b34c1d0 | |||
7b1acc494d | |||
6ff90694e2 | |||
157c7c66b5 | |||
93249258c6 | |||
01c01a46fa | |||
0e2a10fe94 | |||
0b9f3de47c | |||
bc239eead1 | |||
7a09869b0c | |||
bdc0de6ada | |||
6870a7f9b1 | |||
ace1a1b063 | |||
d87b879cc3 | |||
65b36f8e69 | |||
87387a54f2 | |||
b497cebe76 | |||
0fa107a75b | |||
b34e3aeed0 | |||
35b18d72fd | |||
4b80f30afb | |||
86c54e04a8 | |||
ef9fc596f5 | |||
ad34e455ce | |||
01fdd0bee2 | |||
32ffa345cd | |||
6fc3c16073 | |||
7d58657c80 | |||
fcd657c10e | |||
4ab77c5557 | |||
2bbaa500f4 | |||
722a20e141 | |||
041c24a837 | |||
39a3baa00b | |||
f7acbb1eaa | |||
e6fbf7aa5b | |||
87926e69db | |||
36a084eab6 | |||
a9e319e0c0 | |||
ea23b97231 | |||
7df76f9642 | |||
f6db117a5e | |||
23c3cd605d | |||
77690702c0 | |||
e0093480d9 | |||
c7679b7a67 | |||
e6ac34bde8 | |||
8c4b595c30 | |||
be629e5c6b | |||
63569684a3 | |||
5937a0d7ce | |||
4512272c1c | |||
ce1f3842e0 | |||
ee01c3a059 | |||
81c1537bad | |||
98ecfab032 | |||
b948b8c1a4 | |||
f856c6ae37 | |||
2dd2580530 | |||
f0c100aee4 |
24
.drone.yml
24
.drone.yml
@@ -4,18 +4,6 @@ type: docker
|
||||
name: build:dev
|
||||
|
||||
steps:
|
||||
- name: build dev
|
||||
image: plugins/docker
|
||||
depends_on: [clone]
|
||||
settings:
|
||||
username:
|
||||
from_secret: DOCKER_REGISTRY_USER
|
||||
password:
|
||||
from_secret: DOCKER_REGISTRY_PASSWORD
|
||||
repo: registry.odit.services/lfk/frontend
|
||||
tags:
|
||||
- dev
|
||||
registry: registry.odit.services
|
||||
- name: run full license export
|
||||
depends_on: ["clone"]
|
||||
image: node:alpine
|
||||
@@ -33,6 +21,18 @@ steps:
|
||||
remote: git@git.odit.services:lfk/frontend.git
|
||||
ssh_key:
|
||||
from_secret: GITLAB_SSHKEY
|
||||
- name: build dev
|
||||
image: plugins/docker
|
||||
depends_on: [clone]
|
||||
settings:
|
||||
username:
|
||||
from_secret: DOCKER_REGISTRY_USER
|
||||
password:
|
||||
from_secret: DOCKER_REGISTRY_PASSWORD
|
||||
repo: registry.odit.services/lfk/frontend
|
||||
tags:
|
||||
- dev
|
||||
registry: registry.odit.services
|
||||
|
||||
trigger:
|
||||
branch:
|
||||
|
15
.gitignore
vendored
15
.gitignore
vendored
@@ -1,10 +1,11 @@
|
||||
.vscode
|
||||
.idea
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
public/env.js
|
||||
/build
|
||||
yarn.lock
|
||||
build
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
*.map
|
||||
public/env.js
|
||||
public/sw.js
|
||||
public/index.html
|
||||
public/workbox-*.js
|
||||
svelte.config.js
|
||||
public/index.html
|
||||
|
13
.vscode/extensions.json
vendored
Normal file
13
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"2gua.rainbow-brackets",
|
||||
"christian-kohler.npm-intellisense",
|
||||
"remimarsal.prettier-now",
|
||||
"svelte.svelte-vscode",
|
||||
"lokalise.i18n-ally",
|
||||
"fivethree.vscode-svelte-snippets"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"antfu.i18n-ally"
|
||||
]
|
||||
}
|
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"i18n-ally.localesPaths": "src/locales",
|
||||
"i18n-ally.keystyle": "nested"
|
||||
}
|
247
CHANGELOG.md
247
CHANGELOG.md
@@ -2,9 +2,256 @@
|
||||
|
||||
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)
|
||||
- 🔒 ConfirmTeamDeletion in TeamsOverview [`cbcce33`](https://git.odit.services/lfk/frontend/commit/cbcce336d68b0752daeaf4b5608c43ff6fa11c0d)
|
||||
- 🔒 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)
|
||||
- 🐞 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)
|
||||
- 🔒 ConfirmTeamDeletion in TeamDetail [`489244f`](https://git.odit.services/lfk/frontend/commit/489244f1a9636b9807e751073443f1c767f7b8ca)
|
||||
- 🐞 fix deletion in TeamDetail + TeamsOverview [`e3943d8`](https://git.odit.services/lfk/frontend/commit/e3943d868a6131ea561c4000159b77ff70a4af8b)
|
||||
- 🐞 fix component mount in TeamsEmptyState [`ace1a1b`](https://git.odit.services/lfk/frontend/commit/ace1a1b06379d922df2593c3a7aec77d5f958512)
|
||||
- new license file version [CI SKIP] [`aaec5a3`](https://git.odit.services/lfk/frontend/commit/aaec5a3fc94e13c6d29ea16ab110151aa6dc1156)
|
||||
- 🧹 drop tmp modification from UsersOverview [`854db4e`](https://git.odit.services/lfk/frontend/commit/854db4ece8388a70b8a50c5b4c7ce9be974a2616)
|
||||
|
||||
#### [0.3.0](https://git.odit.services/lfk/frontend/compare/0.2.1...0.3.0)
|
||||
|
||||
> 15 January 2021
|
||||
|
||||
- Merge pull request 'feature/16-org-management' (#32) from feature/16-org-management into dev [`#16`](https://git.odit.services/lfk/frontend/issues/16)
|
||||
- Merge commit 'fcd657c10ea14290455cfb0bf2de89375a664143' into dev [`#31`](https://git.odit.services/lfk/frontend/issues/31)
|
||||
- ✨ added Org base components [`bdc0de6`](https://git.odit.services/lfk/frontend/commit/bdc0de6adab2db95e6075a993bd50317e9e36eb5)
|
||||
- 🚀RELEASE v0.3.0 [`7cd24cd`](https://git.odit.services/lfk/frontend/commit/7cd24cd51d52efff22f4ee0817f2d89f6efb35ed)
|
||||
- 🏬 OrgDetail ui [`7a09869`](https://git.odit.services/lfk/frontend/commit/7a09869b0ca9d5dd38cf0c0010b71ab9ce6c6f40)
|
||||
- 🏁 finish basic functionality of AddOrgModal + OrgDetail [`9324925`](https://git.odit.services/lfk/frontend/commit/93249258c6b0f34da22f0ed5d290b437d221a8fd)
|
||||
- ✨ basic functionality in OrgDetail [`0e2a10f`](https://git.odit.services/lfk/frontend/commit/0e2a10fe94075b3cda8ef3ebae6a6159a2e6bbf9)
|
||||
- 🐞 fix sidebar mobile-md scaling [`fcd657c`](https://git.odit.services/lfk/frontend/commit/fcd657c10ea14290455cfb0bf2de89375a664143)
|
||||
- 🔨 reorder CI build order for correct license exporting [`7d58657`](https://git.odit.services/lfk/frontend/commit/7d58657c800e9e494c1d1f098ad0dc34d1f0cd8d)
|
||||
- 💬 AddOrgModal bindings [`bc239ee`](https://git.odit.services/lfk/frontend/commit/bc239eead1d1a29c9662d94db797c6bf23e43166)
|
||||
- ✨ improvements in OrgOverview [`0b9f3de`](https://git.odit.services/lfk/frontend/commit/0b9f3de47c0c8b750c38da07e53927ba55ef3499)
|
||||
- 🔨 config compatibility for new Snowpack V3 bundler [`32ffa34`](https://git.odit.services/lfk/frontend/commit/32ffa345cde67b1affe2750add620522c0a24577)
|
||||
- 🧹 general component cleanup [`157c7c6`](https://git.odit.services/lfk/frontend/commit/157c7c66b57c3a5053c780682402d3615fbc046a)
|
||||
- ⏫ basic dependency bump [`6fc3c16`](https://git.odit.services/lfk/frontend/commit/6fc3c16073555fffd55773218c5c2eed361f076b)
|
||||
- 🌎 i18n [`01c01a4`](https://git.odit.services/lfk/frontend/commit/01c01a46faa9332a2e437861fdb42afe962da833)
|
||||
- ⏫ bump gridjs to 3.2.2 [`6ff9069`](https://git.odit.services/lfk/frontend/commit/6ff90694e288356e16f5226a6e0a054e6fec8b3f)
|
||||
- [tmp] - disable serviceworker [`0fa107a`](https://git.odit.services/lfk/frontend/commit/0fa107a75bc33046758bc42523262a566da9f60f)
|
||||
- new license file version [CI SKIP] [`ad34e45`](https://git.odit.services/lfk/frontend/commit/ad34e455ceedc0e0ccca5ccd86f7f011a7667873)
|
||||
- ⏫ Bump Dockerfile builder to 15.5.1-alpine3.12 [`01fdd0b`](https://git.odit.services/lfk/frontend/commit/01fdd0bee2199bb58373aecc9942591a2d45011a)
|
||||
- new license file version [CI SKIP] [`87926e6`](https://git.odit.services/lfk/frontend/commit/87926e69dbd5b93b2c98cd68fb31913259e3cc00)
|
||||
- 🐳 Dockerfile - drop js sourcemaps [`4b80f30`](https://git.odit.services/lfk/frontend/commit/4b80f30afbfb8c16b9395fb310f309c516c48a17)
|
||||
|
||||
#### [0.2.1](https://git.odit.services/lfk/frontend/compare/0.2.0...0.2.1)
|
||||
|
||||
> 13 January 2021
|
||||
|
||||
- 🔒 UserDetail - WIP on Permissions [`36a084e`](https://git.odit.services/lfk/frontend/commit/36a084eab6bd0c922da29ebb2d260ba803bf9675)
|
||||
- 👪 UserDetail - group edit support [`a9e319e`](https://git.odit.services/lfk/frontend/commit/a9e319e0c0f2bfaba42bc3af875462e394489dc2)
|
||||
- 🙋♂️ UserDetails - group updating [`041c24a`](https://git.odit.services/lfk/frontend/commit/041c24a837d58ff46362699b54e8088f22ba2daa)
|
||||
- ⚡ shared state reactivity - AddUserModal-Users-UsersOverview [`f7acbb1`](https://git.odit.services/lfk/frontend/commit/f7acbb1eaa14ddf41e29ff1db631520123218289)
|
||||
- ✨ AddUserModal + UserDetail - optional username field [`7df76f9`](https://git.odit.services/lfk/frontend/commit/7df76f9642b528586fd654f27148b5502400ffdf)
|
||||
- UserDetail - fixed group updating [`e6fbf7a`](https://git.odit.services/lfk/frontend/commit/e6fbf7aa5b230733ffd92fdfe851b9f86001c859)
|
||||
- 🚀RELEASE v0.2.1 [`2bbaa50`](https://git.odit.services/lfk/frontend/commit/2bbaa500f4ca56e41613cac12228f4e3327180ab)
|
||||
- 💬 UserDetail - info Toasts [`ea23b97`](https://git.odit.services/lfk/frontend/commit/ea23b972315db39e1f800366d52e3bacf9eaf58b)
|
||||
- 🔨 optimized release script [`23c3cd6`](https://git.odit.services/lfk/frontend/commit/23c3cd605db950a5620ea709748216f7ea034385)
|
||||
- 🐞 fix package release script: locales directory [`722a20e`](https://git.odit.services/lfk/frontend/commit/722a20e141079302da8876be1ccc55026c588048)
|
||||
- ⏫ bump @odit/lfk-client-js to 0.0.11 [`f6db117`](https://git.odit.services/lfk/frontend/commit/f6db117a5eca5c9692a4c5d83159917a712a0e63)
|
||||
- 🐞 UserDetail - fix permission reactivity by assignments [`39a3baa`](https://git.odit.services/lfk/frontend/commit/39a3baa00b0575ee3150c7ae26f32995e2e857d9)
|
||||
|
||||
#### [0.2.0](https://git.odit.services/lfk/frontend/compare/0.1.6...0.2.0)
|
||||
|
||||
> 11 January 2021
|
||||
|
||||
- 🔒 added rendering based on permission level [`5937a0d`](https://git.odit.services/lfk/frontend/commit/5937a0d7ce970d38ddb0e4c6a02d11f8c8b53e9b)
|
||||
- ❌ UserDetail - delete [`4512272`](https://git.odit.services/lfk/frontend/commit/4512272c1c2a5c861d8ac4c9908244df125dc3d9)
|
||||
- 🖊 UserDetail - reactivity on edit + update functionality [`ce1f384`](https://git.odit.services/lfk/frontend/commit/ce1f3842e0f581d9e94ea6079cf223d945f7465d)
|
||||
- 🚀RELEASE v0.2.0 [`7769070`](https://git.odit.services/lfk/frontend/commit/77690702c0461b71ff1b65a44bfdc331d5a42c3e)
|
||||
- [tmp] - disable darkmode + re-enable sw [`c7679b7`](https://git.odit.services/lfk/frontend/commit/c7679b7a67b362a7fbf796f612892bdfac50731c)
|
||||
- 🕕 set manual refresh time to 2min [`be629e5`](https://git.odit.services/lfk/frontend/commit/be629e5c6b076bf9a28dcc953e97478d244d8413)
|
||||
- 🐞 [tmp] - nginx.conf - disable .js file caching [`e6ac34b`](https://git.odit.services/lfk/frontend/commit/e6ac34bde8d288a45e83fc6723a2bbe952f2622a)
|
||||
- ℹ update jwtinfo store on token refresh [`6356968`](https://git.odit.services/lfk/frontend/commit/63569684a392bf0c24c9c2efd7945114d1a230a5)
|
||||
|
||||
#### [0.1.6](https://git.odit.services/lfk/frontend/compare/0.1.5...0.1.6)
|
||||
|
||||
> 10 January 2021
|
||||
|
||||
- ✨ UsersOverview - user delete [`f0c100a`](https://git.odit.services/lfk/frontend/commit/f0c100aee47d6a5aeb0995db79b268f33bf316e9)
|
||||
- 🔒 UserDetail - added basic layout for permission change [`81c1537`](https://git.odit.services/lfk/frontend/commit/81c1537bada2c87127385d9110d48459cd1b505f)
|
||||
- 🚀RELEASE v0.1.6 [`ee01c3a`](https://git.odit.services/lfk/frontend/commit/ee01c3a059c435add68938657955cbd2c5851c50)
|
||||
- 📧 UserDetail - email input [`f856c6a`](https://git.odit.services/lfk/frontend/commit/f856c6ae3792c93aee148339d89e3978db6e9293)
|
||||
- ✨ UserDetail multiselect layout for groups [`98ecfab`](https://git.odit.services/lfk/frontend/commit/98ecfab0325e35c5418775f7162049b56e5f332f)
|
||||
- UserDetail - placeholder for permission picker 🔒 [`b948b8c`](https://git.odit.services/lfk/frontend/commit/b948b8c1a48e5b4bc6a083139d26100ef4499970)
|
||||
|
||||
#### [0.1.5](https://git.odit.services/lfk/frontend/compare/0.1.4...0.1.5)
|
||||
|
||||
> 10 January 2021
|
||||
|
||||
- Merge commit '16f572480ad55425890061f9dad65fe85f2f39ad' into dev [`#30`](https://git.odit.services/lfk/frontend/issues/30)
|
||||
- 🚀RELEASE v0.1.5 [`330755c`](https://git.odit.services/lfk/frontend/commit/330755c63e3405533a5da79c84ef4f379eb43e1c)
|
||||
- ⤵ load dynamic build info in Footer component [`c089bb3`](https://git.odit.services/lfk/frontend/commit/c089bb39298fb1067093c2fa81101130c214947c)
|
||||
- 📅 dynamic copyright year in Footer component [`b8a9e4f`](https://git.odit.services/lfk/frontend/commit/b8a9e4f272f925999b9a032dd009f7498acbfae0)
|
||||
- 👀 improved Footer layout + display on Login component [`43b4065`](https://git.odit.services/lfk/frontend/commit/43b406592ebe115cea04a8dbf36874c0a5bdd7e9)
|
||||
|
@@ -1,12 +1,11 @@
|
||||
FROM node:15.4.0-alpine3.12
|
||||
FROM node:15.5.1-alpine3.12
|
||||
WORKDIR /app
|
||||
RUN npm i -g pnpm
|
||||
COPY package.json ./
|
||||
RUN pnpm i
|
||||
COPY package.json *.config.js workbox-config.js ./
|
||||
COPY package.json *.config.js workbox-config.js template-copy.js index.template.html s-config.template.js ./
|
||||
COPY src ./src
|
||||
COPY public ./public
|
||||
RUN pnpm run build:sw
|
||||
RUN pnpm run build
|
||||
# final image
|
||||
FROM alpine
|
||||
|
@@ -10,10 +10,11 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="Lauf Für Kaya! - Admin" />
|
||||
<title>Lauf für Kaya! - Admin</title>
|
||||
__TAILWIND_INSERT__
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.1.5-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>
|
@@ -9,7 +9,7 @@ http {
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
|
||||
location ~* \.(?:ico|css|gif|jpe?g|png)$ {
|
||||
expires 1y;
|
||||
add_header Pragma public;
|
||||
add_header Cache-Control "public";
|
||||
|
46
package.json
46
package.json
@@ -1,41 +1,47 @@
|
||||
{
|
||||
"name": "@odit/lfk-frontend",
|
||||
"version": "0.1.5",
|
||||
"version": "0.6.0",
|
||||
"scripts": {
|
||||
"i18n-order": "node order.js",
|
||||
"dev": "snowpack dev",
|
||||
"build": "snowpack build",
|
||||
"dev:all": "yarn prebuild && 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",
|
||||
"release": "release-it",
|
||||
"changelog": "npx auto-changelog --commit-limit false --template https://raw.githubusercontent.com/release-it/release-it/master/templates/changelog-compact.hbs",
|
||||
"licenses:export": "license-exporter --json -o public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@odit/lfk-client-js": "0.0.10",
|
||||
"@odit/lfk-client-js": "0.4.5",
|
||||
"csvtojson": "^2.0.10",
|
||||
"filepond": "4.25.1",
|
||||
"gridjs": "3.2.1",
|
||||
"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.4.1",
|
||||
"@snowpack/plugin-svelte": "3.5.2",
|
||||
"auto-changelog": "^2.2.1",
|
||||
"autoprefixer": "10.2.1",
|
||||
"postcss": "8.2.4",
|
||||
"postcss-load-config": "3.0.0",
|
||||
"release-it": "^14.2.2",
|
||||
"snowpack": "3.0.0-rc.2",
|
||||
"svelte": "3.31.2",
|
||||
"svelte-preprocess": "4.6.1",
|
||||
"workbox-cli": "6.0.2"
|
||||
"autoprefixer": "10.2.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"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": {
|
||||
@@ -51,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"
|
||||
"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
1
public/imprint_en.md
Normal 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.
|
File diff suppressed because one or more lines are too long
@@ -1,11 +1,18 @@
|
||||
{
|
||||
"name": "Lauf für Kaya! - Admin",
|
||||
"short_name": "LfK!Admin",
|
||||
"start_url": ".",
|
||||
"start_url": "/?utm_source=pwa",
|
||||
"orientation": "portrait-primary",
|
||||
"display": "standalone",
|
||||
"background_color": "#fff",
|
||||
"theme_color": "#fff",
|
||||
"description": "Lauf für Kaya! - Admin",
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Users",
|
||||
"url": "/users/?utm_source=pwa"
|
||||
}
|
||||
],
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.png",
|
||||
|
1
public/privacy_en.md
Normal file
1
public/privacy_en.md
Normal 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.
|
@@ -1,2 +0,0 @@
|
||||
if(!self.define){const e=e=>{"require"!==e&&(e+=".js");let r=Promise.resolve();return i[e]||(r=new Promise((async r=>{if("document"in self){const i=document.createElement("script");i.src=e,document.head.appendChild(i),i.onload=r}else importScripts(e),r()}))),r.then((()=>{if(!i[e])throw new Error(`Module ${e} didn’t register its module`);return i[e]}))},r=(r,i)=>{Promise.all(r.map(e)).then((e=>i(1===e.length?e[0]:e)))},i={require:Promise.resolve(r)};self.define=(r,s,o)=>{i[r]||(i[r]=Promise.resolve().then((()=>{let i={};const c={uri:location.origin+r.slice(1)};return Promise.all(s.map((r=>{switch(r){case"exports":return i;case"module":return c;default:return e(r)}}))).then((e=>{const r=o(...e);return i.default||(i.default=r),i}))})))}}define("./sw.js",["./workbox-c8ead010"],(function(e){"use strict";self.addEventListener("message",(e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()})),e.precacheAndRoute([{url:"favicon.ico",revision:"ba44f340afba5bb1a07f14decc15dd04"},{url:"favicon.png",revision:"07a9941cec62319578fa2a1734db9959"},{url:"favicon.svg",revision:"689d6c6fda51e359c0e5725d9e905064"},{url:"index.html",revision:"931c34f3675364dcc09411aa0f223776"},{url:"logo.svg",revision:"4c9e31a1f4268d7e36e22cda7656e561"},{url:"manifest.webmanifest",revision:"75c93eb352c4877216e77b1d7f73445f"},{url:"robots.txt",revision:"61c27d2cd39a713f7829422c3d9edcc7"}],{})}));
|
||||
//# sourceMappingURL=sw.js.map
|
File diff suppressed because one or more lines are too long
6
s-config.template.js
Normal file
6
s-config.template.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const sveltePreprocess = require('svelte-preprocess');
|
||||
const preprocess = sveltePreprocess(__insert__);
|
||||
|
||||
module.exports = {
|
||||
preprocess
|
||||
};
|
@@ -5,10 +5,11 @@ module.exports = {
|
||||
src: '/_dist_'
|
||||
},
|
||||
plugins: [ '@snowpack/plugin-svelte' ],
|
||||
install: [
|
||||
/* ... */
|
||||
routes: [
|
||||
/* Enable an SPA Fallback in development: */
|
||||
{ match: 'routes', src: '.*', dest: '/index.html' }
|
||||
],
|
||||
installOptions: {
|
||||
packageOptions: {
|
||||
/* ... */
|
||||
sourceMap: false
|
||||
},
|
||||
@@ -18,13 +19,8 @@ module.exports = {
|
||||
buildOptions: {
|
||||
/* ... */
|
||||
},
|
||||
proxy: {
|
||||
/* ... */
|
||||
},
|
||||
alias: {
|
||||
/* ... */
|
||||
},
|
||||
experiments: {
|
||||
optimize: { bundle: true, minify: true }
|
||||
}
|
||||
optimize: { bundle: true, minify: true }
|
||||
};
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import "./TailwindStyles.svelte";
|
||||
import "toastify-js/src/toastify.css";
|
||||
import "gridjs/dist/theme/mermaid.css";
|
||||
import { Route, router } from "tinro";
|
||||
router.subscribe((routeInfo) => {
|
||||
window.scrollTo(0, 0);
|
||||
@@ -30,7 +31,6 @@
|
||||
import Login from "./components/Login.svelte";
|
||||
import Dashboard from "./components/Dashboard.svelte";
|
||||
import store from "./store.js";
|
||||
import NotFound from "./components/NotFound.svelte";
|
||||
import ForgotPassword from "./components/ForgotPassword.svelte";
|
||||
import MainDashContent from "./components/MainDashContent.svelte";
|
||||
import Users from "./components/Users.svelte";
|
||||
@@ -40,16 +40,21 @@
|
||||
import Orgs from "./components/Orgs.svelte";
|
||||
import Runners from "./components/Runners.svelte";
|
||||
import Footer from "./components/Footer.svelte";
|
||||
import Tracks from "./components/Tracks.svelte";
|
||||
import TracksOverview from "./components/TracksOverview.svelte";
|
||||
import OrgDetail from "./components/OrgDetail.svelte";
|
||||
import Teams from "./components/Teams.svelte";
|
||||
import { OpenAPI, AuthService } from "@odit/lfk-client-js";
|
||||
import { OpenAPI } from "@odit/lfk-client-js";
|
||||
import UserDetail from "./components/UserDetail.svelte";
|
||||
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();
|
||||
registerSW();
|
||||
</script>
|
||||
|
||||
<Route>
|
||||
@@ -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,11 +103,21 @@
|
||||
</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">
|
||||
<Teams />
|
||||
<Route path="/teams/*">
|
||||
<Route path="/">
|
||||
<Teams />
|
||||
</Route>
|
||||
<Route path="/:teamid" let:params>
|
||||
<TeamDetail {params} />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route path="/orgs/*">
|
||||
<Route path="/">
|
||||
|
@@ -163,7 +163,35 @@
|
||||
</div>
|
||||
{/await}
|
||||
</div>
|
||||
<h2 class="text-4xl font-display font-semibold md:text-5xl">{$_('faq')}</h2>
|
||||
<div class="w-full leading-8 mt-8">
|
||||
<p class="text-xl font-medium">{$_('icon-image-credits')}</p>
|
||||
<ul class="list-disc">
|
||||
<li>
|
||||
<a
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://storyset.com">https://storyset.com</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://undraw.co">https://undraw.co</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
class="underline"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href="https://remixicon.com">https://remixicon.com</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h2 class="mt-4 text-4xl font-display font-semibold md:text-5xl">
|
||||
{$_('faq')}
|
||||
</h2>
|
||||
<div class="mt-6 border-t-2 border-gray-100 pt-10">
|
||||
<dl class="md:grid md:grid-cols-2 md:gap-8">
|
||||
<div>
|
||||
|
162
src/components/AddOrgModal.svelte
Normal file
162
src/components/AddOrgModal.svelte
Normal file
@@ -0,0 +1,162 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "./outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
import { RunnerOrganizationService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
export let modal_open;
|
||||
export let current_organizations;
|
||||
let name_input_dom;
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
$: name = "";
|
||||
$: processed_last_submit = true;
|
||||
$: isOrgnameValid = name.trim().length !== 0;
|
||||
$: createbtnenabled = isOrgnameValid;
|
||||
(() => {
|
||||
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: "Organization is being added...",
|
||||
duration: -1,
|
||||
}).showToast();
|
||||
RunnerOrganizationService.runnerOrganizationControllerPost({
|
||||
name,
|
||||
address: undefined,
|
||||
contact: undefined,
|
||||
})
|
||||
.then((result) => {
|
||||
console.log(result);
|
||||
name = "";
|
||||
modal_open = false;
|
||||
//
|
||||
Toastify({
|
||||
text: "Organization added",
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_organizations = current_organizations.concat([result]);
|
||||
})
|
||||
.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">​</span>
|
||||
<div
|
||||
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-headline">
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div
|
||||
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<svg
|
||||
class="h-6 w-6 text-blue-600"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"><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>
|
||||
</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 Organization
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
Please provide the required information to add a new
|
||||
organization.
|
||||
</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">Name</label>
|
||||
<input
|
||||
use:focus
|
||||
autocomplete="off"
|
||||
placeholder="Name"
|
||||
class:border-red-500={!isOrgnameValid}
|
||||
class:focus:border-red-500={!isOrgnameValid}
|
||||
class:focus:ring-red-500={!isOrgnameValid}
|
||||
bind:value={name}
|
||||
bind:this={name_input_dom}
|
||||
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 !isOrgnameValid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
Organization name 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}
|
312
src/components/AddRunnerModal.svelte
Normal file
312
src/components/AddRunnerModal.svelte
Normal 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">​</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}
|
||||
>
|
||||
{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}
|
181
src/components/AddTeamModal.svelte
Normal file
181
src/components/AddTeamModal.svelte
Normal file
@@ -0,0 +1,181 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "./outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
import {
|
||||
RunnerOrganizationService,
|
||||
RunnerTeamService,
|
||||
} from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
export let modal_open;
|
||||
export let current_teams;
|
||||
let teamname_input_dom;
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
$: teamname = "";
|
||||
$: processed_last_submit = true;
|
||||
$: isTeamNameValid = teamname.trim().length !== 0;
|
||||
$: createbtnenabled = isTeamNameValid;
|
||||
(function () {
|
||||
document.onkeydown = function (e) {
|
||||
e = e || window.event;
|
||||
if (e.key === "Escape") {
|
||||
modal_open = false;
|
||||
}
|
||||
if (e.keyCode === 13) {
|
||||
if (createbtnenabled === true) {
|
||||
createbtnenabled = false;
|
||||
submit();
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
$: parentGroup = undefined;
|
||||
$: orgs = [];
|
||||
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
|
||||
orgs = val;
|
||||
});
|
||||
function submit() {
|
||||
if (processed_last_submit === true) {
|
||||
processed_last_submit = false;
|
||||
const toast = Toastify({
|
||||
text: "Team is being added...",
|
||||
duration: -1,
|
||||
}).showToast();
|
||||
RunnerTeamService.runnerTeamControllerPost({
|
||||
parentGroup,
|
||||
name: teamname,
|
||||
})
|
||||
.then((result) => {
|
||||
teamname = "";
|
||||
modal_open = false;
|
||||
//
|
||||
Toastify({
|
||||
text: "Team added",
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
current_teams.push(result);
|
||||
current_teams = current_teams;
|
||||
})
|
||||
.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">​</span>
|
||||
<div
|
||||
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-headline">
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div
|
||||
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<svg
|
||||
class="h-6 w-6 text-blue-600"
|
||||
fill="currentColor"
|
||||
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>
|
||||
</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 team
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
Please provide the required information to add a new team.
|
||||
</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">{$_('team-name')}</label>
|
||||
<input
|
||||
use:focus
|
||||
autocomplete="off"
|
||||
placeholder={$_('team-name')}
|
||||
class:border-red-500={!isTeamNameValid}
|
||||
class:focus:border-red-500={!isTeamNameValid}
|
||||
class:focus:ring-red-500={!isTeamNameValid}
|
||||
bind:value={teamname}
|
||||
bind:this={teamname_input_dom}
|
||||
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 !isTeamNameValid}
|
||||
<span
|
||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||
team name is required
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="firstname"
|
||||
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 rounded-md p-2">
|
||||
{#each orgs as t}
|
||||
<option value={t.id}>{t.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</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}
|
@@ -2,20 +2,21 @@
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "./outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
import { tracks as tracksstore } from "../store.js";
|
||||
import { UserService } from "@odit/lfk-client-js";
|
||||
import isEmail from "validator/es/lib/isEmail";
|
||||
import Toastify from "toastify-js";
|
||||
import "toastify-js/src/toastify.css";
|
||||
export let modal_open;
|
||||
export let current_users;
|
||||
let firstname_input;
|
||||
let lastname_input;
|
||||
let middlename_input;
|
||||
let username_input;
|
||||
let password_input;
|
||||
let email_input;
|
||||
function focus(el) {
|
||||
el.focus();
|
||||
}
|
||||
$: username_input_value = "";
|
||||
$: middlename_input_value = "";
|
||||
$: password_input_value = "";
|
||||
$: email_input_value = "";
|
||||
@@ -27,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;
|
||||
@@ -53,13 +54,16 @@
|
||||
firstname: firstname_input_value,
|
||||
lastname: lastname_input_value,
|
||||
middlename: middlename_input_value,
|
||||
email: email_input_value,
|
||||
password: password_input_value,
|
||||
email: email_input_value,
|
||||
username: username_input_value,
|
||||
})
|
||||
.then((result) => {
|
||||
firstname_input_value = "";
|
||||
lastname_input_value = "";
|
||||
middlename_input_value = "";
|
||||
email_input_value = "";
|
||||
username_input_value = "";
|
||||
modal_open = false;
|
||||
//
|
||||
Toastify({
|
||||
@@ -67,12 +71,8 @@
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
let storeval = [];
|
||||
tracksstore.subscribe((val) => {
|
||||
storeval = val;
|
||||
});
|
||||
storeval.push(result);
|
||||
tracksstore.set(storeval);
|
||||
current_users.push(result);
|
||||
current_users = current_users;
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
@@ -213,6 +213,19 @@
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="col-span-6">
|
||||
<label
|
||||
for="trackname"
|
||||
class="block text-sm font-medium text-gray-700">{$_('username')}</label>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('username')}
|
||||
bind:value={username_input_value}
|
||||
bind:this={username_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="email"
|
||||
@@ -220,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"
|
||||
@@ -230,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>
|
||||
|
@@ -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>
|
||||
|
100
src/components/ConfirmOrgDeletion.svelte
Normal file
100
src/components/ConfirmOrgDeletion.svelte
Normal file
@@ -0,0 +1,100 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "./outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
import { RunnerOrganizationService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let modal_open;
|
||||
export let delete_org;
|
||||
const dispatch = createEventDispatcher();
|
||||
function cancelDelete() {
|
||||
modal_open = false;
|
||||
dispatch("cancelDelete", { id: delete_org.id });
|
||||
}
|
||||
function deleteOrg() {
|
||||
RunnerOrganizationService.runnerOrganizationControllerRemove(
|
||||
delete_org.id,
|
||||
true
|
||||
)
|
||||
.then((resp) => {
|
||||
Toastify({
|
||||
text: "Organization deleted",
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
location.replace("./");
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
use:clickOutside
|
||||
on:click_outside={cancelDelete}>
|
||||
<div
|
||||
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
||||
<div
|
||||
class="absolute inset-0 bg-gray-500 opacity-75"
|
||||
data-id="modal_backdrop" />
|
||||
</div>
|
||||
<span
|
||||
class="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
<div
|
||||
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-headline">
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div
|
||||
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<svg
|
||||
class="h-6 w-6 text-blue-600"
|
||||
fill="currentColor"
|
||||
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>
|
||||
</div>
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
Attention!
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
Do you want to delete the organization
|
||||
{delete_org.name}?<br />All associated teams and runners will
|
||||
be deleted too!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
on:click={deleteOrg}
|
||||
type="button"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
Confirm, delete organization and associated teams+runners.
|
||||
</button>
|
||||
<button
|
||||
on:click={cancelDelete}
|
||||
type="button"
|
||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
Cancel, keep organization
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
97
src/components/ConfirmTeamDeletion.svelte
Normal file
97
src/components/ConfirmTeamDeletion.svelte
Normal file
@@ -0,0 +1,97 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { clickOutside } from "./outsideclick";
|
||||
import { focusTrap } from "svelte-focus-trap";
|
||||
import { RunnerTeamService } from "@odit/lfk-client-js";
|
||||
import Toastify from "toastify-js";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
export let modal_open;
|
||||
export let delete_team;
|
||||
const dispatch = createEventDispatcher();
|
||||
function cancelDelete() {
|
||||
modal_open = false;
|
||||
dispatch("cancelDelete", { id: delete_team.id });
|
||||
}
|
||||
function deleteTeam() {
|
||||
RunnerTeamService.runnerTeamControllerRemove(delete_team.id, true)
|
||||
.then((resp) => {
|
||||
Toastify({
|
||||
text: "Team deleted",
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
location.replace("./");
|
||||
})
|
||||
.catch((err) => {
|
||||
//
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if modal_open}
|
||||
<div
|
||||
class="fixed z-10 inset-0 overflow-y-auto"
|
||||
use:focusTrap
|
||||
use:clickOutside
|
||||
on:click_outside={cancelDelete}>
|
||||
<div
|
||||
class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div class="fixed inset-0 transition-opacity" aria-hidden="true">
|
||||
<div
|
||||
class="absolute inset-0 bg-gray-500 opacity-75"
|
||||
data-id="modal_backdrop" />
|
||||
</div>
|
||||
<span
|
||||
class="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
<div
|
||||
class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-headline">
|
||||
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
|
||||
<div class="sm:flex sm:items-start">
|
||||
<div
|
||||
class="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<svg
|
||||
class="h-6 w-6 text-blue-600"
|
||||
fill="currentColor"
|
||||
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>
|
||||
</div>
|
||||
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
Attention!
|
||||
</h3>
|
||||
<div class="mt-2 mb-6">
|
||||
<p class="text-sm text-gray-500">
|
||||
Do you want to delete the team
|
||||
{delete_team.name}?<br />All associated runners will be
|
||||
deleted too!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
on:click={deleteTeam}
|
||||
type="button"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
Confirm, delete team and associated runners.
|
||||
</button>
|
||||
<button
|
||||
on:click={cancelDelete}
|
||||
type="button"
|
||||
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
Cancel, keep team
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
@@ -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
@@ -1,52 +1,29 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import { active } from "tinro";
|
||||
import localForage from "localforage";
|
||||
import { router } from "tinro";
|
||||
|
||||
import store from "../store";
|
||||
import { router } from "tinro";
|
||||
import NoComponentLoaded from "./NoComponentLoaded.svelte";
|
||||
import { AuthService, OpenAPI } from "@odit/lfk-client-js";
|
||||
|
||||
let activePage = "dashboard";
|
||||
import { AuthService } from "@odit/lfk-client-js";
|
||||
let dropdown1 = false;
|
||||
let navOpen = false;
|
||||
function ismobile() {
|
||||
let check = false;
|
||||
(function (a) {
|
||||
if (
|
||||
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
|
||||
a
|
||||
) ||
|
||||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
|
||||
a.substr(0, 4)
|
||||
)
|
||||
)
|
||||
check = true;
|
||||
})(navigator.userAgent || navigator.vendor || window.opera);
|
||||
return check;
|
||||
}
|
||||
$: mobile = ismobile();
|
||||
$: navOpen = false;
|
||||
function logout() {
|
||||
localForage.clear();
|
||||
location.replace("/");
|
||||
}
|
||||
</script>
|
||||
|
||||
<section class="min-h-screen bg-gray-50 dark:bg-black dark:text-gray-100">
|
||||
<section class="min-h-screen bg-gray-50">
|
||||
<nav
|
||||
class:hidden={!navOpen && mobile}
|
||||
class="select-none fixed top-0 left-0 z-20 h-full pb-10 overflow-x-hidden overflow-y-auto transition origin-left transform border-r w-60 md:translate-x-0">
|
||||
class:-translate-x-full={!navOpen}
|
||||
class:translate-x-0={navOpen}
|
||||
class="select-none fixed top-0 left-0 z-20 h-full pb-10 overflow-x-hidden overflow-y-auto transition origin-left transform border-r w-60 md:translate-x-0 bg-gray-50">
|
||||
<a href="/" class="flex items-center px-4 py-5">
|
||||
<img
|
||||
src="/lfk-logo.png"
|
||||
alt="Logo"
|
||||
class="h-10" />
|
||||
<img src="/lfk-logo.png" alt="Logo" class="h-10" />
|
||||
<h3 class="text-lg">Lauf für Kaya! Admin</h3>
|
||||
</a>
|
||||
<nav class="text-sm font-medium text-gray-600" aria-label="Main Navigation">
|
||||
<a
|
||||
class:dark:bg-gray-900={$router.path === '/'}
|
||||
class:bg-gray-100={$router.path === '/'}
|
||||
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
|
||||
href="/">
|
||||
@@ -60,184 +37,92 @@
|
||||
</svg>
|
||||
<span>{$_('dashboard-title')}</span>
|
||||
</a>
|
||||
<a
|
||||
class:dark:bg-gray-900={$router.path.includes('/orgs/')}
|
||||
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:dark:bg-gray-900={$router.path === '/users/'}
|
||||
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:dark:bg-gray-900={$router.path === '/runners/'}
|
||||
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:dark:bg-gray-900={$router.path === '/teams/'}
|
||||
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:dark:bg-gray-900={$router.path === '/tracks/'}
|
||||
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>
|
||||
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:dark:bg-gray-900={activePage === 'blub'}
|
||||
class:bg-gray-100={activePage === 'blub'}
|
||||
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:dark:bg-gray-900={activePage === 'blub'}
|
||||
class:bg-gray-100={activePage === 'blub'}
|
||||
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>
|
||||
<a
|
||||
class:dark:bg-gray-900={$router.path === '/settings/'}
|
||||
class:bg-gray-100={$router.path === '/settings/'}
|
||||
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
|
||||
href="/settings/">
|
||||
@@ -254,7 +139,6 @@
|
||||
<span>{$_('settings')}</span>
|
||||
</a>
|
||||
<a
|
||||
class:dark:bg-gray-900={$router.path === '/about/'}
|
||||
class:bg-gray-100={$router.path === '/about/'}
|
||||
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
|
||||
href="/about/">
|
||||
@@ -291,65 +175,14 @@
|
||||
</nav>
|
||||
</nav>
|
||||
<div class="ml-0 transition md:ml-60">
|
||||
<header
|
||||
class="flex items-center justify-between w-full px-4 border-b h-14">
|
||||
<button
|
||||
on:click={() => {
|
||||
navOpen = !navOpen;
|
||||
}}
|
||||
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 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<!-- <div class="hidden -ml-3 form-icon md:block w-96">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg>
|
||||
<input
|
||||
class="border-0 form-input"
|
||||
placeholder="Search for articles..." />
|
||||
</div> -->
|
||||
<div class="flex items-end">
|
||||
<a href="#" class="flex text-gray-500">
|
||||
<svg
|
||||
class="flex-shrink-0 w-5 h-5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path
|
||||
d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a href="/profile/" class="ml-4">
|
||||
<img
|
||||
class="h-8 w-8 rounded-full"
|
||||
src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
alt="Profile Picture" />
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<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>
|
||||
</div>
|
||||
<!-- Sidebar Backdrop -->
|
||||
<div
|
||||
on:click={() => {
|
||||
navOpen = false;
|
||||
|
@@ -2,7 +2,6 @@
|
||||
import { _, json } from "svelte-i18n";
|
||||
import { getlang } from "./datatable_i18n";
|
||||
import { Grid } from "gridjs";
|
||||
import "gridjs/dist/theme/mermaid.css";
|
||||
//
|
||||
let table;
|
||||
const datatable = new Grid({
|
||||
|
@@ -1,9 +1,13 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { _ } from "svelte-i18n";
|
||||
const releaseinfo = document
|
||||
.getElementById("buildinfo")
|
||||
.textContent.replace("RELEASE_INFO-", "")
|
||||
.replace("-RELEASE_INFO", "");
|
||||
$: releaseinfo = "";
|
||||
onMount(() => {
|
||||
releaseinfo = document
|
||||
.getElementById("buildinfo")
|
||||
.textContent.replace("RELEASE_INFO-", "")
|
||||
.replace("-RELEASE_INFO", "");
|
||||
});
|
||||
const year = new Date().getFullYear();
|
||||
</script>
|
||||
|
||||
@@ -28,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>
|
||||
|
364
src/components/ImportRunnerModal.svelte
Normal file
364
src/components/ImportRunnerModal.svelte
Normal 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">​</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}
|
||||
>
|
||||
{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}
|
45
src/components/Imprint.svelte
Normal file
45
src/components/Imprint.svelte
Normal 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 />
|
@@ -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>
|
@@ -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>
|
||||
|
@@ -1,83 +1,239 @@
|
||||
<script>
|
||||
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;
|
||||
export let params;
|
||||
let orgdata = {};
|
||||
let original = {};
|
||||
$: data_loaded = false;
|
||||
$: data_changed = JSON.stringify(orgdata) === JSON.stringify(original);
|
||||
const promise = RunnerOrganizationService.runnerOrganizationControllerGetOne(
|
||||
params.orgid
|
||||
).then((value) => {
|
||||
data_loaded = true;
|
||||
orgdata = Object.assign(orgdata, value);
|
||||
original = Object.assign(original, value);
|
||||
});
|
||||
let modal_open = false;
|
||||
let delete_org = {};
|
||||
function deleteOrganization() {
|
||||
// RunnerOrganizationService.runnerOrganizationControllerRemove(
|
||||
// original.id,
|
||||
// false
|
||||
// )
|
||||
// .then((resp) => {
|
||||
// Toastify({
|
||||
// text: "Organization deleted",
|
||||
// duration: 500,
|
||||
// backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
// }).showToast();
|
||||
// location.replace("./");
|
||||
// })
|
||||
// .catch((err) => {
|
||||
modal_open = true;
|
||||
delete_org = original;
|
||||
// });
|
||||
}
|
||||
function submit() {
|
||||
if (data_loaded === true && save_enabled) {
|
||||
Toastify({
|
||||
text: "updating organization",
|
||||
duration: 2500,
|
||||
}).showToast();
|
||||
RunnerOrganizationService.runnerOrganizationControllerPut(
|
||||
original.id,
|
||||
orgdata
|
||||
)
|
||||
.then((resp) => {
|
||||
Object.assign(original, orgdata);
|
||||
original = orgdata;
|
||||
Object.assign(original, orgdata);
|
||||
//
|
||||
Toastify({
|
||||
text: "updated organization",
|
||||
duration: 2500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
})
|
||||
.catch((err) => {});
|
||||
} else {
|
||||
}
|
||||
}
|
||||
export let import_modal_open = false;
|
||||
</script>
|
||||
|
||||
<section class="container p-5">
|
||||
<span class="mb-1 text-3xl font-extrabold leading-tight">
|
||||
Orgs
|
||||
</span>
|
||||
<p class="mb-8 text-lg text-gray-500">
|
||||
configure the tracks & minimum lap times
|
||||
</p>
|
||||
<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="mr-2 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 stroke-current"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"><path
|
||||
d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
<polyline points="9 22 9 12 15 12 15 22" /></svg>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="/">Home</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="mr-2 flex items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z" /></svg>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="./">Orgs</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">Org-Details #{params.orgid}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<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={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={() => {
|
||||
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-organization')}</button>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !delete_triggered}
|
||||
<button
|
||||
on:click={submit}
|
||||
disabled={!save_enabled}
|
||||
class:opacity-50={!save_enabled}
|
||||
type="button"
|
||||
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<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="mr-2 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 stroke-current"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"><path
|
||||
d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
<polyline points="9 22 9 12 15 12 15 22" /></svg>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="/">Home</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="mr-2 flex items-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
height="24"><path fill="none" d="M0 0h24v24H0z" />
|
||||
<path
|
||||
d="M21 20h2v2H1v-2h2V3a1 1 0 0 1 1-1h16a1 1 0 0 1 1 1v17zm-2 0V4H5v16h14zM8 11h3v2H8v-2zm0-4h3v2H8V7zm0 8h3v2H8v-2zm5 0h3v2h-3v-2zm0-4h3v2h-3v-2zm0-4h3v2h-3V7z" /></svg>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="./">Orgs</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">Org-Details #{params.orgid}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
<label for="name" class="font-medium text-gray-700">Name</label>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Name"
|
||||
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 rounded-md p-2" />
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
<label
|
||||
for="contact"
|
||||
class="font-medium text-gray-700">{$_('contact')}</label>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('contact')}
|
||||
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 rounded-md p-2" />
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
<label
|
||||
for="address"
|
||||
class="font-medium text-gray-700">{$_('address')}</label>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('address')}
|
||||
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 rounded-md p-2" />
|
||||
</div>
|
||||
</section>
|
||||
{:else}
|
||||
{#await promise}
|
||||
organization detail is being loaded...
|
||||
{:catch error}
|
||||
<PromiseError />
|
||||
{/await}
|
||||
{/if}
|
||||
|
174
src/components/OrgOverview.svelte
Normal file
174
src/components/OrgOverview.svelte
Normal file
@@ -0,0 +1,174 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
let modal_open = false;
|
||||
let delete_org = {};
|
||||
import { RunnerOrganizationService } from "@odit/lfk-client-js";
|
||||
import store from "../store";
|
||||
import OrgsEmptyState from "./OrgsEmptyState.svelte";
|
||||
import Toastify from "toastify-js";
|
||||
import ConfirmOrgDeletion from "./ConfirmOrgDeletion.svelte";
|
||||
$: searchvalue = "";
|
||||
$: active_deletes = [];
|
||||
export let current_organizations = [];
|
||||
|
||||
const promise = RunnerOrganizationService.runnerOrganizationControllerGetAll().then(
|
||||
(val) => {
|
||||
current_organizations = val;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<ConfirmOrgDeletion
|
||||
on:cancelDelete={(event) => {
|
||||
modal_open = false;
|
||||
active_deletes[event.detail.id] = false;
|
||||
}}
|
||||
bind:modal_open
|
||||
bind:delete_org />
|
||||
{#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"
|
||||
role="alert">
|
||||
<p class="font-bold">organizations are being loaded...</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_organizations.length === 0}
|
||||
<OrgsEmptyState />
|
||||
{:else}
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||
<table class="divide-y divide-gray-200 w-full">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Address
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Contact
|
||||
</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_organizations as o}
|
||||
{#if Object.values(o)
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchvalue)}
|
||||
<tr data-rowid="org_{o.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">
|
||||
{o.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="ml-4">
|
||||
<div
|
||||
class="text-sm font-medium text-gray-900">
|
||||
{#if o.address}
|
||||
{JSON.stringify(o.address)}
|
||||
{:else}no address specified{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="ml-4">
|
||||
<div
|
||||
class="text-sm font-medium text-gray-900">
|
||||
{#if o.contact}
|
||||
{JSON.stringify(o.contact)}
|
||||
{:else}no contact specified{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{#if active_deletes[o.id] === true}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[o.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel
|
||||
Delete</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
RunnerOrganizationService.runnerOrganizationControllerRemove(o.id, false)
|
||||
.then((resp) => {
|
||||
current_organizations = current_organizations.filter((obj) => obj.id !== o.id);
|
||||
Toastify({
|
||||
text: 'Organization deleted',
|
||||
duration: 500,
|
||||
backgroundColor:
|
||||
'linear-gradient(to right, #00b09b, #96c93d)',
|
||||
}).showToast();
|
||||
})
|
||||
.catch((err) => {
|
||||
modal_open = true;
|
||||
delete_org = o;
|
||||
});
|
||||
}}
|
||||
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="./{o.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900">Edit</a>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:DELETE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[o.id] = true;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</button>
|
||||
{/if}
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
{:catch error}
|
||||
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
|
||||
<span class="inline-block align-middle mr-8">
|
||||
<b class="capitalize">{$_('general_promise_error')}</b>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
@@ -1,13 +1,52 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import store from "../store";
|
||||
import AddOrgModal from "./AddOrgModal.svelte";
|
||||
export let modal_open = false;
|
||||
import OrgOverview from "./OrgOverview.svelte";
|
||||
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">
|
||||
Orgs
|
||||
{$_('organizations')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('ORGANIZATION:CREATE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
modal_open = true;
|
||||
}}
|
||||
type="button"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
{$_('create-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">
|
||||
add, delete, edit organizations
|
||||
</p>
|
||||
<nav><a class="underline" href="./1">Org 1</a></nav>
|
||||
<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('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}
|
||||
|
17
src/components/OrgsEmptyState.svelte
Normal file
17
src/components/OrgsEmptyState.svelte
Normal file
@@ -0,0 +1,17 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import AddOrgModal from "./AddOrgModal.svelte";
|
||||
import org_empty from "./org_empty.svg";
|
||||
let modal_open = false;
|
||||
let current_organizations = [];
|
||||
</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={org_empty} alt="" />
|
||||
<span class="font-bold">There are no organizations added yet.</span><br />
|
||||
<span>Add your first organization</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<AddOrgModal bind:modal_open bind:current_organizations />
|
45
src/components/Privacy.svelte
Normal file
45
src/components/Privacy.svelte
Normal 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 />
|
@@ -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
|
||||
|
129
src/components/ResetPassword.svelte
Normal file
129
src/components/ResetPassword.svelte
Normal 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}
|
264
src/components/RunnerDetail.svelte
Normal file
264
src/components/RunnerDetail.svelte
Normal 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}
|
||||
>
|
||||
{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}
|
@@ -1,10 +1,49 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
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>
|
||||
|
||||
{#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}
|
||||
|
16
src/components/RunnersEmptyState.svelte
Normal file
16
src/components/RunnersEmptyState.svelte
Normal file
@@ -0,0 +1,16 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
// import AddUserModal from "./AddUserModal.svelte";
|
||||
import runners_empty from "./runners_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={runners_empty} alt="" />
|
||||
<span class="font-bold">There are no runners added yet.</span><br />
|
||||
<span>Add your first runner</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- <AddUserModal bind:modal_open /> -->
|
187
src/components/RunnersOverview.svelte
Normal file
187
src/components/RunnersOverview.svelte
Normal 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}
|
@@ -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">
|
||||
|
245
src/components/TeamDetail.svelte
Normal file
245
src/components/TeamDetail.svelte
Normal file
@@ -0,0 +1,245 @@
|
||||
<script>
|
||||
import {
|
||||
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;
|
||||
let [teamdata, original, delete_team, orgs, modal_open] = [
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
[],
|
||||
false,
|
||||
];
|
||||
export let import_modal_open = false;
|
||||
$: delete_triggered = false;
|
||||
$: save_enabled = !data_changed;
|
||||
$: data_loaded = false;
|
||||
$: data_changed = JSON.stringify(teamdata) === JSON.stringify(original);
|
||||
//
|
||||
const promise = RunnerTeamService.runnerTeamControllerGetOne(
|
||||
params.teamid
|
||||
).then((value) => {
|
||||
data_loaded = true;
|
||||
teamdata = Object.assign(teamdata, value);
|
||||
original = Object.assign(original, value);
|
||||
});
|
||||
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
|
||||
orgs = val;
|
||||
});
|
||||
function deleteTeam() {
|
||||
RunnerTeamService.runnerTeamControllerRemove(original.id, false)
|
||||
.then((resp) => {
|
||||
Toastify({
|
||||
text: "Organization deleted",
|
||||
duration: 500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
location.replace("./");
|
||||
})
|
||||
.catch((err) => {
|
||||
modal_open = true;
|
||||
delete_team = original;
|
||||
});
|
||||
}
|
||||
function submit() {
|
||||
if (data_loaded === true && save_enabled) {
|
||||
Toastify({
|
||||
text: "updating team",
|
||||
duration: 2500,
|
||||
}).showToast();
|
||||
teamdata.parentGroup = teamdata.parentGroup.id;
|
||||
RunnerTeamService.runnerTeamControllerPut(original.id, teamdata)
|
||||
.then((resp) => {
|
||||
Object.assign(original, teamdata);
|
||||
original = teamdata;
|
||||
Object.assign(original, teamdata);
|
||||
//
|
||||
Toastify({
|
||||
text: "updated team",
|
||||
duration: 2500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
})
|
||||
.catch((err) => {});
|
||||
} else {
|
||||
}
|
||||
}
|
||||
</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
|
||||
on:click={deleteTeam}
|
||||
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-delete')}</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
delete_triggered = !delete_triggered;
|
||||
}}
|
||||
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button>
|
||||
{/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-team')}</button>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !delete_triggered}
|
||||
<button
|
||||
on:click={submit}
|
||||
disabled={!save_enabled}
|
||||
class:opacity-50={!save_enabled}
|
||||
type="button"
|
||||
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('save-changes')}</button>
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
<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="mr-2 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 stroke-current"
|
||||
height="1em"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"><path
|
||||
d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
||||
<polyline points="9 22 9 12 15 12 15 22" /></svg>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="/">Home</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="mr-2 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="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>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<a class="mr-2" href="./">Teams</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">Team-Details #{params.teamid}</span>
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
<label for="name" class="font-medium text-gray-700">Name</label>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder="Name"
|
||||
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 rounded-md p-2" />
|
||||
</div>
|
||||
<div class="text-sm w-full">
|
||||
<label
|
||||
for="contact"
|
||||
class="font-medium text-gray-700">{$_('contact')}</label>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('contact')}
|
||||
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 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 rounded-md p-2">
|
||||
{#each orgs as o}
|
||||
<option value={o.id}>{o.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
{:else}
|
||||
{#await promise}
|
||||
team detail is being loaded...
|
||||
{:catch error}
|
||||
<PromiseError />
|
||||
{/await}
|
||||
{/if}
|
@@ -1,10 +1,31 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import store from "../store";
|
||||
import AddTeamModal from "./AddTeamModal.svelte";
|
||||
export let modal_open = false;
|
||||
import TeamsOverview from "./TeamsOverview.svelte";
|
||||
console.log(store.state.jwtinfo.userdetails.permissions);
|
||||
let current_teams=[];
|
||||
</script>
|
||||
|
||||
<section class="container p-5">
|
||||
<span class="mb-1 text-3xl font-extrabold leading-tight">
|
||||
{$_('teams')}
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
modal_open = true;
|
||||
}}
|
||||
type="button"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
Create Team
|
||||
</button>
|
||||
{/if}
|
||||
</span>
|
||||
<p class="mb-8 text-lg text-gray-500">everything is more fun together 🏃♂️🏃♀️🏃♂️</p>
|
||||
<TeamsOverview bind:current_teams />
|
||||
</section>
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')}
|
||||
<AddTeamModal bind:current_teams bind:modal_open />
|
||||
{/if}
|
||||
|
17
src/components/TeamsEmptyState.svelte
Normal file
17
src/components/TeamsEmptyState.svelte
Normal file
@@ -0,0 +1,17 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import AddTeamModal from "./AddTeamModal.svelte";
|
||||
import team_empty from "./team_empty.svg";
|
||||
let modal_open = false;
|
||||
let current_teams = [];
|
||||
</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={team_empty} alt="" />
|
||||
<span class="font-bold">There are no teams added yet.</span><br />
|
||||
<span>Add your first team</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<AddTeamModal bind:modal_open bind:current_teams />
|
175
src/components/TeamsOverview.svelte
Normal file
175
src/components/TeamsOverview.svelte
Normal file
@@ -0,0 +1,175 @@
|
||||
<script>
|
||||
import { t, _ } from "svelte-i18n";
|
||||
import Toastify from "toastify-js";
|
||||
import { RunnerTeamService } from "@odit/lfk-client-js";
|
||||
const teams_promise = RunnerTeamService.runnerTeamControllerGetAll();
|
||||
import { users as usersstore } from "../store.js";
|
||||
import store from "../store";
|
||||
import TeamsEmptyState from "./TeamsEmptyState.svelte";
|
||||
import ConfirmTeamDeletion from "./ConfirmTeamDeletion.svelte";
|
||||
$: searchvalue = "";
|
||||
$: active_deletes = [];
|
||||
export let current_teams = [];
|
||||
let modal_open = false;
|
||||
let delete_team = {};
|
||||
usersstore.subscribe((val) => {
|
||||
current_teams = val;
|
||||
});
|
||||
teams_promise.then((data) => {
|
||||
usersstore.set(data);
|
||||
});
|
||||
</script>
|
||||
|
||||
<ConfirmTeamDeletion
|
||||
on:cancelDelete={(event) => {
|
||||
modal_open = false;
|
||||
active_deletes[event.detail.id] = false;
|
||||
}}
|
||||
bind:modal_open
|
||||
bind:delete_team />
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:GET')}
|
||||
{#await teams_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">teams are being loaded...</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_teams.length === 0}
|
||||
<TeamsEmptyState />
|
||||
{:else}
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
<div
|
||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||
<table class="divide-y divide-gray-200 w-full">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Name
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{$_('organization')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Contact
|
||||
</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_teams as t}
|
||||
{#if Object.values(t)
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchvalue)}
|
||||
<tr data-rowid="team_{t.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">
|
||||
{t.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{#if t.parentGroup}
|
||||
<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>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
<div class="ml-4">
|
||||
<div class="text-sm font-medium text-gray-900">
|
||||
{#if t.contact}
|
||||
{JSON.stringify(t.contact)}
|
||||
{:else}no contact specified{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{#if active_deletes[t.id] === true}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[t.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel
|
||||
Delete</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
RunnerTeamService.runnerTeamControllerRemove(t.id, false)
|
||||
.then((resp) => {
|
||||
current_teams = current_teams.filter((obj) => obj.id !== t.id);
|
||||
Toastify({
|
||||
text: 'Organization deleted',
|
||||
duration: 500,
|
||||
backgroundColor:
|
||||
'linear-gradient(to right, #00b09b, #96c93d)',
|
||||
}).showToast();
|
||||
})
|
||||
.catch((err) => {
|
||||
modal_open = true;
|
||||
delete_team = t;
|
||||
});
|
||||
}}
|
||||
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="./{t.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900">Edit</a>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM:DELETE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[t.id] = true;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</button>
|
||||
{/if}
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
{:catch error}
|
||||
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
|
||||
<span class="inline-block align-middle mr-8">
|
||||
<b class="capitalize">{$_('general_promise_error')}</b>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
@@ -6,7 +6,6 @@
|
||||
const tracks_promise = TrackService.trackControllerGetAll();
|
||||
import { getlang } from "./datatable_i18n";
|
||||
import { Grid, html } from "gridjs";
|
||||
import "gridjs/dist/theme/mermaid.css";
|
||||
import { tracks as tracksstore } from "../store.js";
|
||||
$: trackscache = [];
|
||||
$: blocked = [];
|
||||
|
@@ -1,28 +1,114 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import lodashIsEqual from "lodash.isequal";
|
||||
import { UserService } from "@odit/lfk-client-js";
|
||||
import "gridjs/dist/theme/mermaid.css";
|
||||
import store from "../store";
|
||||
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 original_data = undefined;
|
||||
$: editable_userdata = undefined;
|
||||
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 = {};
|
||||
$: allgroups = [];
|
||||
$: allgroups_ids = [];
|
||||
$: usergroups_array = [];
|
||||
$: 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 = data;
|
||||
original_data = Object.assign(original_data, data);
|
||||
editable_userdata = data;
|
||||
data.groups.forEach((g) => {
|
||||
usergroups_array = usergroups_array.concat([g.id]);
|
||||
});
|
||||
usergroups_array_original = usergroups_array;
|
||||
allgroups.forEach((g) => {
|
||||
allgroups_ids.push(g.id);
|
||||
});
|
||||
});
|
||||
// $: changes_performed = lodashIsEqual(original_data, editable_userdata);
|
||||
$: changes_performed = !lodashIsEqual({ test: 1 }, { test: 1 });
|
||||
UserGroupService.userGroupControllerGetAll().then((data) => {
|
||||
allgroups = data;
|
||||
});
|
||||
$: changes_performed = !lodashIsEqual(original_data, editable_userdata);
|
||||
$: groups_changed =
|
||||
JSON.stringify(usergroups_array) ===
|
||||
JSON.stringify(usergroups_array_original);
|
||||
$: save_enabled =
|
||||
(changes_performed || !groups_changed) && isEmail(editable_userdata.email);
|
||||
function submit() {
|
||||
if (data_loaded === true && changes_performed === true) {
|
||||
console.log("ok, submitting...");
|
||||
} else {
|
||||
console.log("no changes performed");
|
||||
if (data_loaded === true && save_enabled) {
|
||||
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, 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"),
|
||||
duration: 2500,
|
||||
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||
}).showToast();
|
||||
})
|
||||
.catch((err) => {});
|
||||
}
|
||||
}
|
||||
function deleteUser() {
|
||||
UserService.userControllerRemove(original_data.id, true)
|
||||
.then((resp) => {
|
||||
location.replace("./");
|
||||
})
|
||||
.catch((err) => {});
|
||||
}
|
||||
</script>
|
||||
|
||||
{#await user_promise}
|
||||
@@ -63,71 +149,73 @@
|
||||
<polyline points="12 5 19 12 12 19" /></svg>
|
||||
</li>
|
||||
<li class="flex items-center">
|
||||
<span class="mr-2">{user.firstname}
|
||||
{user.middlename || ''}
|
||||
{user.lastname}</span>
|
||||
<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">
|
||||
{user.firstname}
|
||||
{user.middlename || ''}
|
||||
{user.lastname}
|
||||
<span data-id="user_actions_${user.id}">
|
||||
<button
|
||||
class="hidden w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-green-400 text-base font-medium text-white sm:w-auto sm:text-sm"
|
||||
data-userid="${user.id}"
|
||||
onclick="user__delete_cancel()">{$_('cancel')}</button>
|
||||
<button
|
||||
class="hidden w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-500 text-base font-medium text-white sm:w-auto sm:text-sm"
|
||||
data-userid="${user.id}"
|
||||
onclick="user__delete_confirm()">{$_('confirm-delete')}</button>
|
||||
<button
|
||||
type="button"
|
||||
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('delete-user')}</button>
|
||||
<button
|
||||
disabled={!changes_performed}
|
||||
class:opacity-50={!changes_performed}
|
||||
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>
|
||||
<div class="mb-8 text-3xl font-extrabold">
|
||||
{original_data.firstname}
|
||||
{original_data.middlename || ''}
|
||||
{original_data.lastname}
|
||||
<span data-id="user_actions_${editable_userdata.id}">
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:DELETE')}
|
||||
{#if delete_triggered}
|
||||
<button
|
||||
on:click={deleteUser}
|
||||
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">{$_('confirm-delete')}</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
delete_triggered = !delete_triggered;
|
||||
}}
|
||||
class="w-full justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-400 text-base font-medium text-white sm:w-auto sm:text-sm">{$_('cancel')}</button>
|
||||
{/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-user')}</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="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"
|
||||
src={user.profilePic} />
|
||||
<!-- <span
|
||||
class="inline-block h-12 w-12 rounded-full overflow-hidden bg-gray-100"><svg
|
||||
class="h-full w-full text-gray-300"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"><path
|
||||
d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" /></svg></span> -->
|
||||
<button
|
||||
type="button"
|
||||
class="ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Change</button>
|
||||
class="h-20 w-20 rounded-full overflow-hidden bg-gray-100"
|
||||
src={editable_userdata.profilePic} />
|
||||
</div>
|
||||
<!-- -->
|
||||
<div class="mt-3 text-sm w-full">
|
||||
<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
|
||||
@@ -165,6 +253,74 @@
|
||||
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 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" />
|
||||
</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_userdata.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 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"
|
||||
class="font-medium text-gray-700">{$_('username')}</label>
|
||||
<input
|
||||
autocomplete="off"
|
||||
placeholder={$_('username')}
|
||||
type="text"
|
||||
bind:value={editable_userdata.username}
|
||||
name="username"
|
||||
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>
|
||||
<div class="text-sm w-full">
|
||||
<span class="font-medium">{$_('groups')}</span>
|
||||
<!-- svelte-ignore a11y-no-onchange -->
|
||||
<select
|
||||
bind:value={usergroups_array}
|
||||
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"
|
||||
multiple>
|
||||
{#each allgroups as g}
|
||||
{#if usergroups_array.includes(g.id)}
|
||||
<option selected value={g.id}>{g.name}</option>
|
||||
{:else}
|
||||
<option value={g.id}>{g.name}</option>
|
||||
{/if}
|
||||
{/each}
|
||||
</select>
|
||||
</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}
|
||||
<PromiseError {error} />
|
||||
|
241
src/components/UserPermissions.svelte
Normal file
241
src/components/UserPermissions.svelte
Normal 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}
|
@@ -1,23 +1,31 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import store from "../store";
|
||||
import AddUserModal from "./AddUserModal.svelte";
|
||||
export let modal_open = false;
|
||||
import UsersOverview from "./UsersOverview.svelte";
|
||||
console.log(store.state.jwtinfo.userdetails.permissions);
|
||||
let current_users=[];
|
||||
</script>
|
||||
|
||||
<section class="container p-5">
|
||||
<span class="mb-1 text-3xl font-extrabold leading-tight">
|
||||
{$_('users')}
|
||||
<button
|
||||
on:click={() => {
|
||||
modal_open = true;
|
||||
}}
|
||||
type="button"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
{$_('create-user')}
|
||||
</button>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
modal_open = true;
|
||||
}}
|
||||
type="button"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
{$_('create-user')}
|
||||
</button>
|
||||
{/if}
|
||||
</span>
|
||||
<p class="mb-8 text-lg text-gray-500">{$_('manage-admin-users')}</p>
|
||||
<UsersOverview />
|
||||
<UsersOverview bind:current_users />
|
||||
</section>
|
||||
<AddUserModal bind:modal_open />
|
||||
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:CREATE')}
|
||||
<AddUserModal bind:current_users bind:modal_open />
|
||||
{/if}
|
||||
|
@@ -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>
|
||||
|
@@ -1,147 +1,178 @@
|
||||
<script>
|
||||
import { _ } from "svelte-i18n";
|
||||
import Toastify from "toastify-js";
|
||||
import { UserService } from "@odit/lfk-client-js";
|
||||
const users_promise = UserService.userControllerGetAll();
|
||||
import "gridjs/dist/theme/mermaid.css";
|
||||
import { users as usersstore } from "../store.js";
|
||||
import store from "../store";
|
||||
import UsersEmptyState from "./UsersEmptyState.svelte";
|
||||
$: searchvalue = "";
|
||||
$: userscache = [];
|
||||
$: active_deletes = [];
|
||||
export let current_users=[];
|
||||
$: advanced_search = false;
|
||||
usersstore.subscribe((val) => {
|
||||
userscache = val;
|
||||
current_users=val;
|
||||
});
|
||||
users_promise.then((data) => {
|
||||
console.log(data);
|
||||
usersstore.set(data);
|
||||
});
|
||||
</script>
|
||||
|
||||
{#await users_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">users are being loaded...</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
</div>
|
||||
{:then users}
|
||||
{#if userscache.length === 0}
|
||||
<UsersEmptyState />
|
||||
{:else}
|
||||
{#if advanced_search}
|
||||
advanced search
|
||||
{:else}
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
{/if}
|
||||
<button
|
||||
on:click={() => {
|
||||
advanced_search = !advanced_search;
|
||||
}}
|
||||
type="button"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
{#if advanced_search}
|
||||
toggle simple search
|
||||
{:else}toggle advanced search{/if}
|
||||
</button>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:GET')}
|
||||
{#await users_promise}
|
||||
<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">
|
||||
Status
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Groups
|
||||
</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 users as u}
|
||||
{#if Object.values(u)
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchvalue)}
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
{#if u.profilePic}
|
||||
<div class="flex-shrink-0 h-10 w-10">
|
||||
<img
|
||||
class="h-10 w-10 rounded-full"
|
||||
src={u.profilePic}
|
||||
alt="" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="ml-4">
|
||||
<div
|
||||
class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{u.firstname}
|
||||
{u.middlename || ''}
|
||||
{u.lastname}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{u.email || u.username}
|
||||
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">users are being loaded...</p>
|
||||
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||
</div>
|
||||
{:then}
|
||||
{#if current_users.length === 0}
|
||||
<UsersEmptyState />
|
||||
{:else}
|
||||
<!-- {#if advanced_search}
|
||||
advanced search
|
||||
{:else} -->
|
||||
<input
|
||||
type="search"
|
||||
bind:value={searchvalue}
|
||||
placeholder={$_('datatable.search')}
|
||||
aria-label={$_('datatable.search')}
|
||||
class="gridjs-input gridjs-search-input mb-4" />
|
||||
<!-- {/if} -->
|
||||
<!-- <button
|
||||
on:click={() => {
|
||||
advanced_search = !advanced_search;
|
||||
}}
|
||||
type="button"
|
||||
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-gray-600 text-base font-medium text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||
{#if advanced_search}
|
||||
toggle simple search
|
||||
{:else}toggle advanced search{/if}
|
||||
</button> -->
|
||||
<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">
|
||||
Status
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Groups
|
||||
</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_users as u}
|
||||
{#if Object.values(u)
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(searchvalue)}
|
||||
<tr data-rowid="user_{u.id}">
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
<div class="flex items-center">
|
||||
{#if u.profilePic}
|
||||
<div class="flex-shrink-0 h-10 w-10">
|
||||
<img
|
||||
class="h-10 w-10 rounded-full"
|
||||
src={u.profilePic}
|
||||
alt="" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="ml-4">
|
||||
<div
|
||||
class="text-sm font-medium text-gray-900">
|
||||
{u.firstname}
|
||||
{u.middlename || ''}
|
||||
{u.lastname}
|
||||
</div>
|
||||
<div class="text-sm text-gray-500">
|
||||
{u.email || u.username}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if u.enabled}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap">
|
||||
{#if u.enabled}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Active</span>
|
||||
{:else}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Inactive</span>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{#each u.groups as g}
|
||||
<a
|
||||
href="../groups/{g.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a>
|
||||
{/each}
|
||||
</td>
|
||||
{#if active_deletes[u.id] === true}
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[u.id] = false;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-indigo-600 hover:text-indigo-900 cursor-pointer">Cancel
|
||||
Delete</button>
|
||||
<button
|
||||
on:click={() => {
|
||||
UserService.userControllerRemove(u.id, true)
|
||||
.then((resp) => {
|
||||
current_users=current_users.filter(obj=>obj.id!==u.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}
|
||||
<span
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Inactive</span>
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<a
|
||||
href="./{u.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900">Edit</a>
|
||||
{#if store.state.jwtinfo.userdetails.permissions.includes('USER:DELETE')}
|
||||
<button
|
||||
on:click={() => {
|
||||
active_deletes[u.id] = true;
|
||||
}}
|
||||
tabindex="0"
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</button>
|
||||
{/if}
|
||||
</td>
|
||||
{/if}
|
||||
</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
{#each u.groups as g}
|
||||
<a
|
||||
href="../groups/{g.id}"
|
||||
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a>
|
||||
{/each}
|
||||
</td>
|
||||
<td
|
||||
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<a
|
||||
href="./{u.id}"
|
||||
class="text-indigo-600 hover:text-indigo-900">Edit</a>
|
||||
<span
|
||||
tabindex="0"
|
||||
href="#"
|
||||
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Delete</span>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</tr>
|
||||
{/if}
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/if}
|
||||
{:catch error}
|
||||
<div class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-red-500">
|
||||
<span class="inline-block align-middle mr-8">
|
||||
<b class="capitalize">{$_('general_promise_error')}</b>
|
||||
{error}
|
||||
</span>
|
||||
</div>
|
||||
{/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}
|
||||
{/await}
|
||||
{/if}
|
||||
|
1
src/components/org_empty.svg
Normal file
1
src/components/org_empty.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 25 KiB |
1
src/components/runners_empty.svg
Normal file
1
src/components/runners_empty.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 854.63 686"><path fill="#3f3d56" d="M0 600h821v9.053H0zM0 676.947h821V686H0z"/><path d="M750.178 608.328c-.49-.802-12.06-20.12-16.071-60.234-3.68-36.802-1.313-98.836 30.858-185.367 60.947-163.928-14.046-296.194-14.812-297.512l3.7-2.146c.194.334 19.545 34.057 30.977 87.755a382.846 382.846 0 01-15.856 213.394c-60.844 163.648-15.61 241.118-15.146 241.882z" fill="#3f3d56"/><circle cx="726.346" cy="27.795" r="27.795" fill="#3f3d56"/><circle cx="814.007" cy="130.422" r="27.795" fill="#3f3d56"/><circle cx="754.141" cy="198.841" r="27.795" fill="#dfe5ee"/><circle cx="826.835" cy="256.569" r="27.795" fill="#dfe5ee"/><circle cx="732.76" cy="346.368" r="27.795" fill="#3f3d56"/><path d="M766.97 609.35s-27.796-68.418 55.59-119.731zM732.786 608.11s-12.65-72.758-110.557-72.134z" fill="#3f3d56"/><circle cx="136.5" cy="387.5" r="41" fill="#a0616a"/><path d="M143.928 336.504c-7.84-1.925-16.272-2.247-23.868.49-7.824 2.817-14.263 8.708-19 15.542s-7.918 14.613-10.767 22.425a78.442 78.442 0 00-3.845 13.14 44.992 44.992 0 008.167 34.325c-1.2-3.166 1.822-6.617 5.138-7.303s6.693.474 9.963 1.353a61.559 61.559 0 0017.68 2.078c2.284-.065 4.679-.295 6.578-1.565 5.952-3.98 2.802-14.263 7.754-19.434 1.767-1.845 4.29-2.708 6.567-3.865 8.2-4.167 13.604-12.576 16.156-21.413 1.599-5.54 5.567-21.45 2.185-26.598-3.057-4.653-17.516-7.9-22.708-9.175z" fill="#2f2e41"/><path d="M248.066 342.291a24.396 24.396 0 00-18.677 1.041c-10.695 5.016-29.608 17.167-28.889 40.168 1 32 12 53 12 53l-2 65-10 118s-49 40-30 50 51-48 51-48l25-67 9-65s15-68 0-98c0 0 17.328-40.17-7.434-49.209zM367.5 445.5s-17 72 43 92 139 54 139 54l10 17 23-31-4-24-39-11s-73-56-105-60l4-37zM527.307 387.717L548.5 422.5s70 40 88 67 61 56 61 56l-19 45s-54-63-77-69-125-88-125-88z" fill="#a0616a"/><path d="M338.568 265.819c-7.805 1.226-15.617 2.454-23.33 4.174-17.917 3.995-35.147 10.616-51.966 17.974a608.405 608.405 0 00-68.447 35.267 132.255 132.255 0 00-16.97 11.523c-3.559 2.964-6.91 6.363-8.82 10.584a33.406 33.406 0 00-2.33 9.221c-1.55 11.12-1.477 22.595 1.535 33.41s9.124 20.955 18.119 27.674a29.88 29.88 0 007.84 4.348 45.675 45.675 0 0011.675 1.957c12.617 1.003 25.798 1.923 37.474-2.964 7.42-3.105 13.701-8.354 20.528-12.607 28.913-18.008 65.646-17.241 97.26-29.92 3.074-1.233 6.204-2.67 8.354-5.189a22.578 22.578 0 003.62-7.187l11.99-33.188c2.533-7.01 5.067-14.023 7.193-21.167 2.224-7.472 3.996-15.07 5.766-22.661 1.524-6.534 3.093-13.376 2.507-20.13-.864-9.962-3.166-10.367-12.316-8.925q-24.839 3.915-49.682 7.806z" fill="#dfe5ee"/><path d="M218.066 342.291a24.396 24.396 0 00-18.677 1.041c-10.695 5.016-29.608 17.167-28.889 40.168 1 32 12 53 12 53l-2 65-10 118s-49 40-30 50 51-48 51-48l25-67 9-65s15-68 0-98c0 0 17.328-40.17-7.434-49.209z" fill="#a0616a"/><path d="M391.66 257.544S480.5 249.5 476.5 304.5s-34 150-34 150l-77 4s-10-65 4-87z" fill="#2f2e41"/><path d="M453.5 275.5s22 14 34 42 48 83 48 83l-59 42-44-45zM559.5 587.5s-19 7-6 21a126.61 126.61 0 0120 29s-4 26 13 25 24-36 20-48-9-69-9-69-35 3-34 8 6 30-4 34zM678.5 567.5s-18 9-13 17 21 31 21 31-6 30 11 33 27-36 24-44-3-39-3-39 1-31-9-31-27 4-27 4 4 28-4 29z" fill="#2f2e41"/><path fill="#dfe5ee" d="M96 617.871h29v50.612H96z"/></svg>
|
After Width: | Height: | Size: 3.2 KiB |
1
src/components/simple.css
Normal file
1
src/components/simple.css
Normal file
File diff suppressed because one or more lines are too long
1
src/components/team_empty.svg
Normal file
1
src/components/team_empty.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 52 KiB |
1
src/components/users_empty.svg
Normal file
1
src/components/users_empty.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.6 KiB |
@@ -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 ...",
|
||||
@@ -26,6 +33,9 @@
|
||||
"general-stats": "Allgemeine Statistiken",
|
||||
"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",
|
||||
@@ -33,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",
|
||||
|
@@ -4,8 +4,10 @@
|
||||
"about": "About",
|
||||
"action": "Action",
|
||||
"add-your-first-track": "Add your first track",
|
||||
"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",
|
||||
@@ -13,12 +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": {
|
||||
@@ -36,12 +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",
|
||||
@@ -70,8 +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",
|
||||
@@ -79,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...",
|
||||
@@ -87,25 +115,49 @@
|
||||
"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",
|
||||
"organizations": "Organizations",
|
||||
"orgs": "Orgs",
|
||||
"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",
|
||||
@@ -116,8 +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"
|
||||
}
|
@@ -27,6 +27,8 @@ const store = () => {
|
||||
AuthService.authControllerRefresh({ token: state.auth.refresh_token }).then((auth) => {
|
||||
console.log('got new auth');
|
||||
OpenAPI.TOKEN = auth.access_token;
|
||||
const jwtinfo = JSON.parse(atob(auth.access_token.split('.')[1]));
|
||||
state.jwtinfo = jwtinfo;
|
||||
localForage.setItem('logindata', auth);
|
||||
});
|
||||
},
|
||||
@@ -39,8 +41,8 @@ const store = () => {
|
||||
//
|
||||
state.refreshInterval = setInterval(() => {
|
||||
this.refreshAuth();
|
||||
// 4min
|
||||
}, 4 * 60000);
|
||||
// 2min
|
||||
}, 2 * 60000);
|
||||
//
|
||||
return state;
|
||||
});
|
||||
|
@@ -1,11 +0,0 @@
|
||||
const sveltePreprocess = require('svelte-preprocess');
|
||||
|
||||
const preprocess = sveltePreprocess({
|
||||
postcss: {
|
||||
plugins: [ require('tailwindcss'), require('autoprefixer') ]
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
preprocess
|
||||
};
|
@@ -2,7 +2,7 @@ module.exports = {
|
||||
purge: {
|
||||
content: [ './src/**/*.svelte' ]
|
||||
},
|
||||
darkMode: 'media',
|
||||
// darkMode: 'media',
|
||||
variants: {},
|
||||
plugins: [],
|
||||
theme: {
|
||||
|
19
template-copy.js
Normal file
19
template-copy.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const fs = require('fs');
|
||||
let content_svelteconfig = fs.readFileSync('./s-config.template.js', { encoding: 'utf8' });
|
||||
let content_html = fs.readFileSync('./index.template.html', { encoding: 'utf8' });
|
||||
if (process.env.NODE_ENV_ODIT == 'development_fast') {
|
||||
content_html = content_html.replace(
|
||||
'__TAILWIND_INSERT__',
|
||||
'<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@2.0.2/dist/tailwind.min.css">'
|
||||
);
|
||||
content_svelteconfig = content_svelteconfig.replace('__insert__', '{postcss:{}}');
|
||||
} else {
|
||||
content_html = content_html.replace('__TAILWIND_INSERT__', '');
|
||||
content_svelteconfig = content_svelteconfig.replace(
|
||||
'__insert__',
|
||||
"{postcss:{plugins:[require('tailwindcss'),require('autoprefixer')]}}"
|
||||
);
|
||||
}
|
||||
fs.writeFileSync('./public/index.html', content_html);
|
||||
fs.writeFileSync('./svelte.config.js', content_svelteconfig);
|
||||
console.info('dev setup script done');
|
@@ -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);
|
||||
|
@@ -1,6 +1,9 @@
|
||||
module.exports = {
|
||||
globDirectory: 'public',
|
||||
globPatterns: [ '**/*.{js,ico,png,svg,html,webmanifest,txt}' ],
|
||||
globIgnores: [ 'env.js', 'env.sample.js', 'licenses.json' ],
|
||||
swDest: 'public/sw.js'
|
||||
globPatterns: [ '**/*.{js,ico,png,svg,html,webmanifest,txt,json}' ],
|
||||
globIgnores: [ 'env.js', 'env.sample.js' ],
|
||||
swDest: 'public/sw.js',
|
||||
cleanupOutdatedCaches: true,
|
||||
mode: 'production',
|
||||
sourcemap: false
|
||||
};
|
||||
|
Reference in New Issue
Block a user