Compare commits
66 Commits
0.5.0
...
4ef1b7abe8
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ef1b7abe8 | |||
| b01fe050d2 | |||
| 1a4cf211eb | |||
| 4541304fa8 | |||
| 894160f3f7 | |||
| 6a91bd53e2 | |||
| 0f013304ef | |||
| 6f4f4ccb16 | |||
| 7138ca1f5f | |||
| 45e7f6a0d1 | |||
| a7098df9cf | |||
| 054c7faaac | |||
| 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 |
56
CHANGELOG.md
56
CHANGELOG.md
@@ -2,8 +2,63 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
All notable changes to this project will be documented in this file. Dates are displayed in UTC.
|
||||||
|
|
||||||
|
#### [0.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)
|
#### [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/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 '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)
|
- 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)
|
||||||
@@ -18,6 +73,7 @@ All notable changes to this project will be documented in this file. Dates are d
|
|||||||
- UserPermissions - working edit [`10d7955`](https://git.odit.services/lfk/frontend/commit/10d7955f99b1a640eb1ac83764c114a25cfcc07b)
|
- 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)
|
- 🐞 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)
|
- 🧹 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)
|
- ✨ formatting [`6e5a4bc`](https://git.odit.services/lfk/frontend/commit/6e5a4bcb39259fc376262c16ad95555fd9cf7a0b)
|
||||||
- ✨ ImportRunnerModal usage in TeamDetail [`de0bd5f`](https://git.odit.services/lfk/frontend/commit/de0bd5fd57edba75faaa2ba097a2c7fb2f621f9a)
|
- ✨ 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)
|
- 💣 process breaking changes for lib v0.3.0 [`dadb806`](https://git.odit.services/lfk/frontend/commit/dadb80608a55980f7b647d41c91061055287a250)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<span style="display: none;visibility: hidden;" id="buildinfo">RELEASE_INFO-0.5.0-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>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<script src="/env.js"></script>
|
<script src="/env.js"></script>
|
||||||
<script defer type="module" src="/_dist_/index.js"></script>
|
<script defer type="module" src="/_dist_/index.js"></script>
|
||||||
|
|||||||
28
package.json
28
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odit/lfk-frontend",
|
"name": "@odit/lfk-frontend",
|
||||||
"version": "0.5.0",
|
"version": "0.6.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"i18n-order": "node order.js",
|
"i18n-order": "node order.js",
|
||||||
"dev:all": "yarn prebuild && snowpack dev",
|
"dev:all": "yarn prebuild && snowpack dev",
|
||||||
@@ -12,17 +12,19 @@
|
|||||||
"licenses:export": "license-exporter --json -o public"
|
"licenses:export": "license-exporter --json -o public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@odit/lfk-client-js": "0.3.0",
|
"@odit/lfk-client-js": "0.4.5",
|
||||||
"csvtojson": "^2.0.10",
|
"csvtojson": "^2.0.10",
|
||||||
"filepond": "4.25.1",
|
"filepond": "4.25.1",
|
||||||
"gridjs": "3.2.2",
|
"gridjs": "3.3.0",
|
||||||
"localforage": "1.9.0",
|
"localforage": "1.9.0",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
|
"marked": "^2.0.0",
|
||||||
"svelte-filepond": "0.0.1",
|
"svelte-filepond": "0.0.1",
|
||||||
"svelte-focus-trap": "1.0.1",
|
"svelte-focus-trap": "1.0.1",
|
||||||
"svelte-i18n": "3.3.0",
|
"svelte-i18n": "3.3.2",
|
||||||
"tailwindcss": "2.0.2",
|
"svelte-select": "^3.16.1",
|
||||||
"tinro": "0.5.9",
|
"tailwindcss": "2.0.3",
|
||||||
|
"tinro": "0.5.12",
|
||||||
"toastify-js": "1.9.3",
|
"toastify-js": "1.9.3",
|
||||||
"validator": "13.5.2",
|
"validator": "13.5.2",
|
||||||
"xlsx": "^0.16.9"
|
"xlsx": "^0.16.9"
|
||||||
@@ -31,15 +33,15 @@
|
|||||||
"@odit/license-exporter": "0.0.9",
|
"@odit/license-exporter": "0.0.9",
|
||||||
"@snowpack/plugin-svelte": "3.5.2",
|
"@snowpack/plugin-svelte": "3.5.2",
|
||||||
"auto-changelog": "^2.2.1",
|
"auto-changelog": "^2.2.1",
|
||||||
"autoprefixer": "10.2.3",
|
"autoprefixer": "10.2.4",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"postcss": "8.2.4",
|
"postcss": "8.2.6",
|
||||||
"postcss-load-config": "3.0.0",
|
"postcss-load-config": "3.0.1",
|
||||||
"release-it": "^14.2.2",
|
"release-it": "^14.4.0",
|
||||||
"snowpack": "3.0.11",
|
"snowpack": "3.0.11",
|
||||||
"svelte": "3.32.0",
|
"svelte": "3.32.3",
|
||||||
"svelte-preprocess": "4.6.3",
|
"svelte-preprocess": "4.6.8",
|
||||||
"workbox-cli": "6.0.2"
|
"workbox-cli": "6.1.0"
|
||||||
},
|
},
|
||||||
"release-it": {
|
"release-it": {
|
||||||
"git": {
|
"git": {
|
||||||
|
|||||||
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
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.
|
||||||
@@ -50,6 +50,11 @@
|
|||||||
import TeamDetail from "./components/TeamDetail.svelte";
|
import TeamDetail from "./components/TeamDetail.svelte";
|
||||||
import UserPermissions from "./components/UserPermissions.svelte";
|
import UserPermissions from "./components/UserPermissions.svelte";
|
||||||
import RunnerDetail from "./components/RunnerDetail.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";
|
||||||
|
import Contacts from "./components/Contacts.svelte";
|
||||||
|
import ContactDetail from "./components/ContactDetail.svelte";
|
||||||
store.init();
|
store.init();
|
||||||
registerSW();
|
registerSW();
|
||||||
</script>
|
</script>
|
||||||
@@ -59,10 +64,22 @@
|
|||||||
<Route path="/forgot_password">
|
<Route path="/forgot_password">
|
||||||
<ForgotPassword />
|
<ForgotPassword />
|
||||||
</Route>
|
</Route>
|
||||||
|
{:else if $router.path.includes('/reset')}
|
||||||
|
<Route path="/reset/:resetkey" let:params>
|
||||||
|
<ResetPassword {params} />
|
||||||
|
</Route>
|
||||||
{:else if $router.path === '/about'}
|
{:else if $router.path === '/about'}
|
||||||
<Route path="/about">
|
<Route path="/about">
|
||||||
<About />
|
<About />
|
||||||
</Route>
|
</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}
|
{:else if $store.isLoggedIn}
|
||||||
<Dashboard>
|
<Dashboard>
|
||||||
<Transition>
|
<Transition>
|
||||||
@@ -104,6 +121,14 @@
|
|||||||
<TeamDetail {params} />
|
<TeamDetail {params} />
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route path="/contacts/*">
|
||||||
|
<Route path="/">
|
||||||
|
<Contacts />
|
||||||
|
</Route>
|
||||||
|
<Route path="/:contact" let:params>
|
||||||
|
<ContactDetail {params} />
|
||||||
|
</Route>
|
||||||
|
</Route>
|
||||||
<Route path="/orgs/*">
|
<Route path="/orgs/*">
|
||||||
<Route path="/">
|
<Route path="/">
|
||||||
<Orgs />
|
<Orgs />
|
||||||
|
|||||||
439
src/components/AddContactModal.svelte
Normal file
439
src/components/AddContactModal.svelte
Normal file
@@ -0,0 +1,439 @@
|
|||||||
|
<script>
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
|
import { clickOutside } from "./outsideclick";
|
||||||
|
import { focusTrap } from "svelte-focus-trap";
|
||||||
|
import {
|
||||||
|
GroupContactService,
|
||||||
|
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_contacts;
|
||||||
|
$: selected_team = undefined;
|
||||||
|
let firstname_input;
|
||||||
|
let lastname_input;
|
||||||
|
let middlename_input;
|
||||||
|
let phone_input;
|
||||||
|
let email_input;
|
||||||
|
let address_input1;
|
||||||
|
let address_input2;
|
||||||
|
let address_zipcode;
|
||||||
|
let address_city;
|
||||||
|
let teams = [];
|
||||||
|
let orgs = [];
|
||||||
|
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
|
||||||
|
teams = val;
|
||||||
|
});
|
||||||
|
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 = "";
|
||||||
|
$: address_input1_value = "";
|
||||||
|
$: address_input2_value = "";
|
||||||
|
$: address_zipcode_value = "";
|
||||||
|
$: address_city_value = "";
|
||||||
|
$: processed_last_submit = true;
|
||||||
|
$: address_checked = true;
|
||||||
|
$: isPhoneValidOrEmpty =
|
||||||
|
(phone_input_value.includes("+") &&
|
||||||
|
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;
|
||||||
|
$: isAddress1Valid = address_input1_value.trim().length !== 0;
|
||||||
|
$: iszipcodevalid = address_zipcode_value.trim().length !== 0;
|
||||||
|
$: iscityvalid = address_city_value.trim().length !== 0;
|
||||||
|
$: createbtnenabled =
|
||||||
|
isFirstnameValid &&
|
||||||
|
isLastnameValid &&
|
||||||
|
isEmailValidOrEmpty &&
|
||||||
|
isPhoneValidOrEmpty &&
|
||||||
|
((isAddress1Valid && iszipcodevalid && iscityvalid) ||
|
||||||
|
address_checked === false);
|
||||||
|
(() => {
|
||||||
|
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: "Contact is being added...",
|
||||||
|
duration: -1,
|
||||||
|
}).showToast();
|
||||||
|
const groups = teams.concat(orgs).map((g) => g.id);
|
||||||
|
let address = {};
|
||||||
|
if (address_checked === true) {
|
||||||
|
address = {
|
||||||
|
address1: address_input1_value,
|
||||||
|
address2: address_input2_value || "",
|
||||||
|
postalcode: address_zipcode_value,
|
||||||
|
city: address_city_value,
|
||||||
|
country: "DE",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let postdata = {
|
||||||
|
groups,
|
||||||
|
firstname: firstname_input_value,
|
||||||
|
lastname: lastname_input_value,
|
||||||
|
address,
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
GroupContactService.groupContactControllerPost(postdata)
|
||||||
|
.then((result) => {
|
||||||
|
firstname_input_value = "";
|
||||||
|
lastname_input_value = "";
|
||||||
|
middlename_input_value = "";
|
||||||
|
email_input_value = "";
|
||||||
|
modal_open = false;
|
||||||
|
//
|
||||||
|
Toastify({
|
||||||
|
text: "Contact added",
|
||||||
|
duration: 500,
|
||||||
|
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||||
|
}).showToast();
|
||||||
|
current_contacts.push(result);
|
||||||
|
current_contacts = current_contacts;
|
||||||
|
})
|
||||||
|
.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 fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z" /></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-contact')}
|
||||||
|
</h3>
|
||||||
|
<div class="mt-2 mb-6">
|
||||||
|
<p class="text-sm text-gray-500">
|
||||||
|
Please provide the required information to add a new contact.
|
||||||
|
</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"
|
||||||
|
multiple
|
||||||
|
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">
|
||||||
|
{@html $_('the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number')}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="email"
|
||||||
|
class="block text-sm font-medium text-gray-700">{$_('e-mail-adress')}</label>
|
||||||
|
<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 class="flex items-start">
|
||||||
|
<div class="flex items-center h-5">
|
||||||
|
<input
|
||||||
|
bind:checked={address_checked}
|
||||||
|
id="comments"
|
||||||
|
name="comments"
|
||||||
|
type="checkbox"
|
||||||
|
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
|
||||||
|
</div>
|
||||||
|
<div class="ml-3 text-sm">
|
||||||
|
<label
|
||||||
|
for="comments"
|
||||||
|
class="font-medium text-gray-700">Address</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{#if address_checked === true}
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="address1"
|
||||||
|
class="block text-sm font-medium text-gray-700">Address</label>
|
||||||
|
<input
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Address"
|
||||||
|
class:border-red-500={!isAddress1Valid}
|
||||||
|
class:focus:border-red-500={!isAddress1Valid}
|
||||||
|
class:focus:ring-red-500={!isAddress1Valid}
|
||||||
|
bind:value={address_input1_value}
|
||||||
|
bind:this={address_input1}
|
||||||
|
type="text"
|
||||||
|
name="address1"
|
||||||
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
|
{#if !isAddress1Valid}
|
||||||
|
<span
|
||||||
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
|
Address is required
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="address2"
|
||||||
|
class="block text-sm font-medium text-gray-700">Apartment,
|
||||||
|
suite, etc.</label>
|
||||||
|
<input
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Apartment, suite, etc."
|
||||||
|
bind:value={address_input2_value}
|
||||||
|
bind:this={address_input2}
|
||||||
|
type="text"
|
||||||
|
name="address2"
|
||||||
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
|
</div>
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="zipcode"
|
||||||
|
class="block text-sm font-medium text-gray-700">ZIP/
|
||||||
|
postal code</label>
|
||||||
|
<input
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="ZIP/ postal code"
|
||||||
|
class:border-red-500={!iszipcodevalid}
|
||||||
|
class:focus:border-red-500={!iszipcodevalid}
|
||||||
|
class:focus:ring-red-500={!iszipcodevalid}
|
||||||
|
bind:value={address_zipcode_value}
|
||||||
|
bind:this={address_zipcode}
|
||||||
|
type="text"
|
||||||
|
name="zipcode"
|
||||||
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
|
{#if !iszipcodevalid}
|
||||||
|
<span
|
||||||
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
|
Valid zipcode/ postal code is required
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="city"
|
||||||
|
class="block text-sm font-medium text-gray-700">City</label>
|
||||||
|
<input
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="City"
|
||||||
|
class:border-red-500={!iscityvalid}
|
||||||
|
class:focus:border-red-500={!iscityvalid}
|
||||||
|
class:focus:ring-red-500={!iscityvalid}
|
||||||
|
bind:value={address_city_value}
|
||||||
|
bind:this={address_city}
|
||||||
|
type="text"
|
||||||
|
name="city"
|
||||||
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
|
{#if !iscityvalid}
|
||||||
|
<span
|
||||||
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
|
Valid city is required
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
|
||||||
|
<button
|
||||||
|
disabled={!createbtnenabled}
|
||||||
|
class:opacity-50={!createbtnenabled}
|
||||||
|
on:click={submit}
|
||||||
|
type="button"
|
||||||
|
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:ml-3 sm:w-auto sm:text-sm">
|
||||||
|
{$_('create')}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
modal_open = false;
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
|
||||||
|
{$_('cancel')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
$: firstname_input_value = "";
|
$: firstname_input_value = "";
|
||||||
$: processed_last_submit = true;
|
$: processed_last_submit = true;
|
||||||
$: isPhoneValidOrEmpty =
|
$: isPhoneValidOrEmpty =
|
||||||
isMobilePhone(
|
phone_input_value.includes("+")&&isMobilePhone(
|
||||||
phone_input_value
|
phone_input_value
|
||||||
.replaceAll("(", "")
|
.replaceAll("(", "")
|
||||||
.replaceAll(")", "")
|
.replaceAll(")", "")
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
{#if !isPhoneValidOrEmpty}
|
{#if !isPhoneValidOrEmpty}
|
||||||
<span
|
<span
|
||||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
{$_('the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number')}
|
{@html $_('the-provided-phone-number-is-invalid-less-than-br-greater-than-please-enter-a-valid-international-number')}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,10 +28,7 @@
|
|||||||
$: isLastnameValid = lastname_input_value.trim().length !== 0;
|
$: isLastnameValid = lastname_input_value.trim().length !== 0;
|
||||||
$: isFirstnameValid = firstname_input_value.trim().length !== 0;
|
$: isFirstnameValid = firstname_input_value.trim().length !== 0;
|
||||||
$: createbtnenabled =
|
$: createbtnenabled =
|
||||||
isFirstnameValid &&
|
isFirstnameValid && isLastnameValid && isPasswordValid && isEmailValid;
|
||||||
isLastnameValid &&
|
|
||||||
isPasswordValid &&
|
|
||||||
!(!isEmailValid && username_input_value.trim().length === 0);
|
|
||||||
(function () {
|
(function () {
|
||||||
document.onkeydown = function (e) {
|
document.onkeydown = function (e) {
|
||||||
e = e || window.event;
|
e = e || window.event;
|
||||||
@@ -241,17 +238,9 @@
|
|||||||
type="email"
|
type="email"
|
||||||
name="email"
|
name="email"
|
||||||
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
<!-- {#if !isEmailValid}
|
{#if !isEmailValid}
|
||||||
<span
|
<span
|
||||||
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">{$_('valid-email-is-required')}</span>
|
||||||
valid email or username is required
|
|
||||||
</span>
|
|
||||||
{/if} -->
|
|
||||||
{#if !isEmailValid && username_input_value.trim().length === 0}
|
|
||||||
<span
|
|
||||||
class="mt-8 flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
|
||||||
valid email or username is required
|
|
||||||
</span>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
388
src/components/ContactDetail.svelte
Normal file
388
src/components/ContactDetail.svelte
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
<script>
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
|
import store from "../store";
|
||||||
|
import {
|
||||||
|
GroupContactService,
|
||||||
|
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;
|
||||||
|
let orgs = [];
|
||||||
|
let teams = [];
|
||||||
|
export let params;
|
||||||
|
$: delete_triggered = false;
|
||||||
|
$: original_data = {};
|
||||||
|
$: editable = {};
|
||||||
|
$: changes_performed = !(
|
||||||
|
JSON.stringify(original_data) === JSON.stringify(editable)
|
||||||
|
);
|
||||||
|
$: isEmailValid =
|
||||||
|
(editable.email || "") === "" ||
|
||||||
|
(editable.email && isEmail(editable.email || ""));
|
||||||
|
$: isFirstnameValid = editable.firstname !== "";
|
||||||
|
$: isLastnameValid = editable.lastname !== "";
|
||||||
|
$: save_enabled =
|
||||||
|
changes_performed &&
|
||||||
|
isFirstnameValid &&
|
||||||
|
isLastnameValid &&
|
||||||
|
isEmailValid &&
|
||||||
|
isPhoneValidOrEmpty &&
|
||||||
|
((isAddress1Valid && iszipcodevalid && iscityvalid) ||
|
||||||
|
address_checked === false);
|
||||||
|
const promise = GroupContactService.groupContactControllerGetOne(
|
||||||
|
params.contact
|
||||||
|
).then((data) => {
|
||||||
|
data_loaded = true;
|
||||||
|
original_data = Object.assign(original_data, data);
|
||||||
|
editable = Object.assign(editable, original_data);
|
||||||
|
editable.groups = editable.groups.map((g) => g.id);
|
||||||
|
original_data.groups = original_data.groups.map((g) => g.id);
|
||||||
|
editable.address_checked = editable.address.address1 !== null;
|
||||||
|
original_data.address_checked = editable.address.address1 !== null;
|
||||||
|
});
|
||||||
|
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
|
||||||
|
orgs = val;
|
||||||
|
});
|
||||||
|
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
|
||||||
|
teams = val;
|
||||||
|
});
|
||||||
|
$: isPhoneValidOrEmpty =
|
||||||
|
editable.phone?.includes("+") ||
|
||||||
|
editable.phone === "" ||
|
||||||
|
editable.phone === null;
|
||||||
|
$: isAddress1Valid = editable.address?.address1?.trim().length !== 0;
|
||||||
|
$: iszipcodevalid = editable.address?.postalcode?.trim().length !== 0;
|
||||||
|
$: iscityvalid = editable.address?.city?.trim().length !== 0;
|
||||||
|
function submit() {
|
||||||
|
//
|
||||||
|
if (data_loaded === true && save_enabled) {
|
||||||
|
Toastify({
|
||||||
|
text: "Contact is being updated...",
|
||||||
|
duration: 2500,
|
||||||
|
}).showToast();
|
||||||
|
editable.address.country = "DE";
|
||||||
|
if (editable.address_checked === false) {
|
||||||
|
editable.address = {};
|
||||||
|
}
|
||||||
|
delete editable.responseType;
|
||||||
|
delete editable.address_checked;
|
||||||
|
if (editable.email) editable.email = editable.email;
|
||||||
|
if (editable.phone) editable.phone = editable.phone;
|
||||||
|
if (editable.middlename) editable.middlename = editable.middlename;
|
||||||
|
GroupContactService.groupContactControllerPut(original_data.id, editable)
|
||||||
|
.then((resp) => {
|
||||||
|
Object.assign(original_data, editable);
|
||||||
|
original_data = editable;
|
||||||
|
Object.assign(original_data, editable);
|
||||||
|
Toastify({
|
||||||
|
text: "Updated contact!",
|
||||||
|
duration: 2500,
|
||||||
|
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||||
|
}).showToast();
|
||||||
|
})
|
||||||
|
.catch((err) => {});
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function deleteContact() {
|
||||||
|
GroupContactService.groupContactControllerRemove(original_data.id, true)
|
||||||
|
.then((resp) => {
|
||||||
|
location.replace("./");
|
||||||
|
})
|
||||||
|
.catch((err) => {});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#await promise}
|
||||||
|
Loading contact details...
|
||||||
|
{:then}
|
||||||
|
<section class="container p-5 select-none">
|
||||||
|
<div class="flex flex-row mb-4">
|
||||||
|
<div class="w-full">
|
||||||
|
<nav class="w-full flex">
|
||||||
|
<ol class="list-none flex flex-row items-center justify-start">
|
||||||
|
<li class="flex items-center">
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="24"
|
||||||
|
height="24"><path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z" /></svg>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center ml-2">
|
||||||
|
<a class="mr-2" href="./">{$_('contacts')}</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="contact_actions_${editable.id}">
|
||||||
|
{#if store.state.jwtinfo.userdetails.permissions.includes('CONTACT:DELETE')}
|
||||||
|
{#if delete_triggered}
|
||||||
|
<button
|
||||||
|
on:click={deleteContact}
|
||||||
|
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
|
||||||
|
Contact</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"
|
||||||
|
class:border-red-500={!isPhoneValidOrEmpty}
|
||||||
|
class:focus:border-red-500={!isPhoneValidOrEmpty}
|
||||||
|
class:focus:ring-red-500={!isPhoneValidOrEmpty}
|
||||||
|
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" />
|
||||||
|
{#if !isPhoneValidOrEmpty}
|
||||||
|
<span
|
||||||
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
|
valid international phone number is required...
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm w-full">
|
||||||
|
<span class="font-medium text-gray-700">{$_('groups')}</span>
|
||||||
|
<select
|
||||||
|
bind:value={editable.groups}
|
||||||
|
name="team"
|
||||||
|
multiple
|
||||||
|
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="flex items-start mt-2">
|
||||||
|
<div class="flex items-center h-5">
|
||||||
|
<input
|
||||||
|
bind:checked={editable.address_checked}
|
||||||
|
id="comments"
|
||||||
|
name="comments"
|
||||||
|
type="checkbox"
|
||||||
|
class="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" />
|
||||||
|
</div>
|
||||||
|
<div class="ml-3 text-sm">
|
||||||
|
<label for="comments" class="font-medium text-gray-700">Address</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{#if editable.address_checked === true}
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="address1"
|
||||||
|
class="block text-sm font-medium text-gray-700">Address</label>
|
||||||
|
<input
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Address"
|
||||||
|
class:border-red-500={!isAddress1Valid}
|
||||||
|
class:focus:border-red-500={!isAddress1Valid}
|
||||||
|
class:focus:ring-red-500={!isAddress1Valid}
|
||||||
|
bind:value={editable.address.address1}
|
||||||
|
type="text"
|
||||||
|
name="address1"
|
||||||
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
|
{#if !isAddress1Valid}
|
||||||
|
<span
|
||||||
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
|
Address is required
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="address2"
|
||||||
|
class="block text-sm font-medium text-gray-700">Apartment, suite, etc.</label>
|
||||||
|
<input
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="Apartment, suite, etc."
|
||||||
|
bind:value={editable.address.address2}
|
||||||
|
type="text"
|
||||||
|
name="address2"
|
||||||
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
|
</div>
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="zipcode"
|
||||||
|
class="block text-sm font-medium text-gray-700">ZIP/ postal code</label>
|
||||||
|
<input
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="ZIP/ postal code"
|
||||||
|
class:border-red-500={!iszipcodevalid}
|
||||||
|
class:focus:border-red-500={!iszipcodevalid}
|
||||||
|
class:focus:ring-red-500={!iszipcodevalid}
|
||||||
|
bind:value={editable.address.postalcode}
|
||||||
|
type="text"
|
||||||
|
name="zipcode"
|
||||||
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
|
{#if !iszipcodevalid}
|
||||||
|
<span
|
||||||
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
|
Valid zipcode/ postal code is required
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="col-span-6">
|
||||||
|
<label
|
||||||
|
for="city"
|
||||||
|
class="block text-sm font-medium text-gray-700">City</label>
|
||||||
|
<input
|
||||||
|
autocomplete="off"
|
||||||
|
placeholder="City"
|
||||||
|
class:border-red-500={!iscityvalid}
|
||||||
|
class:focus:border-red-500={!iscityvalid}
|
||||||
|
class:focus:ring-red-500={!iscityvalid}
|
||||||
|
bind:value={editable.address.city}
|
||||||
|
type="text"
|
||||||
|
name="city"
|
||||||
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 rounded-md p-2" />
|
||||||
|
{#if !iscityvalid}
|
||||||
|
<span
|
||||||
|
class="flex items-center font-medium tracking-wide text-red-500 text-xs mt-1 ml-1">
|
||||||
|
Valid city is required
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
{:catch error}
|
||||||
|
<PromiseError {error} />
|
||||||
|
{/await}
|
||||||
164
src/components/ContactOverview.svelte
Normal file
164
src/components/ContactOverview.svelte
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
<script>
|
||||||
|
import { t, _ } from "svelte-i18n";
|
||||||
|
import Toastify from "toastify-js";
|
||||||
|
import { GroupContactService } from "@odit/lfk-client-js";
|
||||||
|
const promise = GroupContactService.groupContactControllerGetAll();
|
||||||
|
import { users as usersstore } from "../store.js";
|
||||||
|
import store from "../store";
|
||||||
|
import TeamsEmptyState from "./TeamsEmptyState.svelte";
|
||||||
|
$: searchvalue = "";
|
||||||
|
$: active_deletes = [];
|
||||||
|
export let contacts = [];
|
||||||
|
usersstore.subscribe((val) => {
|
||||||
|
contacts = val;
|
||||||
|
});
|
||||||
|
promise.then((data) => {
|
||||||
|
usersstore.set(data);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM: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">teams are being loaded...</p>
|
||||||
|
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||||
|
</div>
|
||||||
|
{:then}
|
||||||
|
{#if contacts.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 contacts 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 groups{/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={() => {
|
||||||
|
GroupContactService.groupContactControllerRemove(t.id, true)
|
||||||
|
.then((resp) => {
|
||||||
|
contacts = contacts.filter((obj) => obj.id !== t.id);
|
||||||
|
Toastify({
|
||||||
|
text: 'Contact deleted',
|
||||||
|
duration: 500,
|
||||||
|
backgroundColor:
|
||||||
|
'linear-gradient(to right, #00b09b, #96c93d)',
|
||||||
|
}).showToast();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
tabindex="0"
|
||||||
|
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Confirm
|
||||||
|
Delete</button>
|
||||||
|
</td>
|
||||||
|
{:else}
|
||||||
|
<td
|
||||||
|
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
|
<a
|
||||||
|
href="./{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}
|
||||||
29
src/components/Contacts.svelte
Normal file
29
src/components/Contacts.svelte
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<script>
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
|
import store from "../store";
|
||||||
|
import AddContactModal from "./AddContactModal.svelte";
|
||||||
|
import ContactsOverview from "./ContactsOverview.svelte";
|
||||||
|
export let modal_open = false;
|
||||||
|
let current_contacts=[];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<section class="container p-5">
|
||||||
|
<span class="mb-1 text-3xl font-extrabold leading-tight">
|
||||||
|
{$_('contacts')}
|
||||||
|
{#if store.state.jwtinfo.userdetails.permissions.includes('CONTACT: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 Contact
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</span>
|
||||||
|
<ContactsOverview bind:current_contacts />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{#if store.state.jwtinfo.userdetails.permissions.includes('CONTACT:CREATE')}
|
||||||
|
<AddContactModal bind:current_contacts bind:modal_open />
|
||||||
|
{/if}
|
||||||
17
src/components/ContactsEmptyState.svelte
Normal file
17
src/components/ContactsEmptyState.svelte
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<script>
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
|
import AddContactModal from "./AddContactModal.svelte";
|
||||||
|
import team_empty from "./team_empty.svg";
|
||||||
|
let modal_open = false;
|
||||||
|
let current_contacts = [];
|
||||||
|
</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 contacts added yet.</span><br />
|
||||||
|
<span>Add your first contact</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<AddContactModal bind:modal_open bind:current_contacts />
|
||||||
177
src/components/ContactsOverview.svelte
Normal file
177
src/components/ContactsOverview.svelte
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<script>
|
||||||
|
import { _ } from "svelte-i18n";
|
||||||
|
import Toastify from "toastify-js";
|
||||||
|
import { GroupContactService } from "@odit/lfk-client-js";
|
||||||
|
const promise = GroupContactService.groupContactControllerGetAll().then(
|
||||||
|
(result) => {
|
||||||
|
current_contacts = result;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
import store from "../store";
|
||||||
|
import ContactsEmptyState from "./ContactsEmptyState.svelte";
|
||||||
|
$: searchvalue = "";
|
||||||
|
$: active_deletes = [];
|
||||||
|
export let current_contacts = [];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if store.state.jwtinfo.userdetails.permissions.includes('TEAM: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">contacts are being loaded...</p>
|
||||||
|
<p class="text-sm">{$_('this-might-take-a-moment')}</p>
|
||||||
|
</div>
|
||||||
|
{:then}
|
||||||
|
{#if current_contacts.length === 0}
|
||||||
|
<ContactsEmptyState />
|
||||||
|
{: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">
|
||||||
|
Groups
|
||||||
|
</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="relative px-6 py-3">
|
||||||
|
<span class="sr-only">Action</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y divide-gray-200">
|
||||||
|
{#each current_contacts 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.firstname}
|
||||||
|
{t.middlename || ''}
|
||||||
|
{t.lastname}
|
||||||
|
</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.groups.length > 0}
|
||||||
|
{#each t.groups as g}
|
||||||
|
{#if g.responseType === 'RUNNERORGANIZATION'}
|
||||||
|
<a
|
||||||
|
href="../orgs/{g.id}"
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.name}</a>
|
||||||
|
{:else}
|
||||||
|
<a
|
||||||
|
href="../teams/{g.id}"
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{g.parentGroup.name}
|
||||||
|
>
|
||||||
|
{g.name}</a>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{:else}no groups{/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.address.address1 !== null}
|
||||||
|
{t.address.address1}<br />
|
||||||
|
{t.address.address2 || ''}<br />
|
||||||
|
{t.address.postalcode}
|
||||||
|
{t.address.city}
|
||||||
|
{t.address.country}
|
||||||
|
{/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={() => {
|
||||||
|
GroupContactService.groupContactControllerRemove(t.id, false).then(
|
||||||
|
(resp) => {
|
||||||
|
current_contacts = current_contacts.filter(
|
||||||
|
(obj) => obj.id !== t.id
|
||||||
|
);
|
||||||
|
Toastify({
|
||||||
|
text: 'Contact deleted',
|
||||||
|
duration: 500,
|
||||||
|
backgroundColor:
|
||||||
|
'linear-gradient(to right, #00b09b, #96c93d)',
|
||||||
|
}).showToast();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
tabindex="0"
|
||||||
|
class="ml-4 text-red-600 hover:text-red-900 cursor-pointer">Confirm
|
||||||
|
Delete</button>
|
||||||
|
</td>
|
||||||
|
{:else}
|
||||||
|
<td
|
||||||
|
class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
|
<a
|
||||||
|
href="./{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}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -123,98 +123,11 @@
|
|||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
<a
|
<a
|
||||||
class:bg-gray-100={false}
|
class:bg-gray-100={$router.path === '/contacts/'}
|
||||||
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
|
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
|
||||||
href="#">
|
href="/contacts/">
|
||||||
<svg
|
<svg fill="currentColor" 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 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M2 22a8 8 0 1 1 16 0H2zm8-9c-3.315 0-6-2.685-6-6s2.685-6 6-6 6 2.685 6 6-2.685 6-6 6zm10 4h4v2h-4v-2zm-3-5h7v2h-7v-2zm2-5h5v2h-5V7z"/></svg>
|
||||||
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
|
<span>{$_('contacts')}</span>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor">
|
|
||||||
<path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z" />
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm9.707 5.707a1 1 0 00-1.414-1.414L9 12.586l-1.293-1.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
<span>Checklists</span>
|
|
||||||
</a>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
tabindex="0"
|
|
||||||
class:bg-gray-100={false}
|
|
||||||
class="flex items-center justify-between px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
|
|
||||||
role="button"
|
|
||||||
on:click={() => {
|
|
||||||
dropdown1 = !dropdown1;
|
|
||||||
}}>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<svg
|
|
||||||
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor">
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M12.316 3.051a1 1 0 01.633 1.265l-4 12a1 1 0 11-1.898-.632l4-12a1 1 0 011.265-.633zM5.707 6.293a1 1 0 010 1.414L3.414 10l2.293 2.293a1 1 0 11-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0zm8.586 0a1 1 0 011.414 0l3 3a1 1 0 010 1.414l-3 3a1 1 0 11-1.414-1.414L16.586 10l-2.293-2.293a1 1 0 010-1.414z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
<span>Integrations</span>
|
|
||||||
</div>
|
|
||||||
{#if dropdown1}
|
|
||||||
<svg
|
|
||||||
class="flex-shrink-0 w-4 h-4 ml-2 transition transform"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
style="transform:rotate(90deg)"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor">
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
{:else}
|
|
||||||
<svg
|
|
||||||
class="flex-shrink-0 w-4 h-4 ml-2 transition transform"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor">
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
</svg>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{#if dropdown1}
|
|
||||||
<div class="mb-4">
|
|
||||||
<a
|
|
||||||
class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900"
|
|
||||||
href="#">Shopify</a>
|
|
||||||
<a
|
|
||||||
class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900"
|
|
||||||
href="#">Slack</a>
|
|
||||||
<a
|
|
||||||
class="flex items-center py-2 pl-12 pr-4 transition cursor-pointer hover:bg-gray-100 hover:text-gray-900"
|
|
||||||
href="#">Zapier</a>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
class="flex items-center px-4 py-3 transition cursor-pointer group hover:bg-gray-100 hover:text-gray-900"
|
|
||||||
href="#">
|
|
||||||
<svg
|
|
||||||
class="flex-shrink-0 w-5 h-5 mr-2 text-gray-400 transition group-hover:text-gray-600"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor">
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M5 5a3 3 0 015-2.236A3 3 0 0114.83 6H16a2 2 0 110 4h-5V9a1 1 0 10-2 0v1H4a2 2 0 110-4h1.17C5.06 5.687 5 5.35 5 5zm4 1V5a1 1 0 10-1 1h1zm3 0a1 1 0 10-1-1v1h1z"
|
|
||||||
clip-rule="evenodd" />
|
|
||||||
<path d="M9 11H3v5a2 2 0 002 2h4v-7zM11 18h4a2 2 0 002-2v-5h-6v7z" />
|
|
||||||
</svg>
|
|
||||||
<span>{$_('changelog')}</span>
|
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class:bg-gray-100={$router.path === '/settings/'}
|
class:bg-gray-100={$router.path === '/settings/'}
|
||||||
@@ -269,6 +182,10 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="ml-0 transition md:ml-60">
|
<div class="ml-0 transition md:ml-60">
|
||||||
|
<header on:click={() => {
|
||||||
|
navOpen = true;
|
||||||
|
}} class="flex items-center justify-between w-full px-4 bg-white border-b h-14 md:hidden"><button class="block btn btn-light md:hidden">
|
||||||
|
<span class="sr-only">Menu</span><svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentcolor"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4A1 1 0 013 5zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zm0 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path></svg></button></header>
|
||||||
<slot>
|
<slot>
|
||||||
<NoComponentLoaded />
|
<NoComponentLoaded />
|
||||||
</slot>
|
</slot>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<script>
|
<script>
|
||||||
|
import { onMount } from "svelte";
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
$: releaseinfo = "";
|
$: releaseinfo = "";
|
||||||
setTimeout(() => {
|
onMount(() => {
|
||||||
releaseinfo = document
|
releaseinfo = document
|
||||||
.getElementById("buildinfo")
|
.getElementById("buildinfo")
|
||||||
.textContent.replace("RELEASE_INFO-", "")
|
.textContent.replace("RELEASE_INFO-", "")
|
||||||
.replace("-RELEASE_INFO", "");
|
.replace("-RELEASE_INFO", "");
|
||||||
}, 1500);
|
});
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -31,5 +32,9 @@
|
|||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener, noreferrer"
|
rel="noopener, noreferrer"
|
||||||
href="https://git.odit.services/lfk/frontend/src/tag/{releaseinfo}">{releaseinfo}</a>
|
href="https://git.odit.services/lfk/frontend/src/tag/{releaseinfo}">{releaseinfo}</a>
|
||||||
|
-
|
||||||
|
<a class="underline" href="/privacy">{$_('privacy')}</a>
|
||||||
|
-
|
||||||
|
<a class="underline" href="/imprint">{$_('imprint')}</a>
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -5,12 +5,17 @@
|
|||||||
import { clickOutside } from "./outsideclick";
|
import { clickOutside } from "./outsideclick";
|
||||||
import { focusTrap } from "svelte-focus-trap";
|
import { focusTrap } from "svelte-focus-trap";
|
||||||
import Toastify from "toastify-js";
|
import Toastify from "toastify-js";
|
||||||
import { ImportService } from "@odit/lfk-client-js";
|
import {
|
||||||
|
ImportService,
|
||||||
|
RunnerTeamService,
|
||||||
|
RunnerOrganizationService,
|
||||||
|
} from "@odit/lfk-client-js";
|
||||||
import { createEventDispatcher } from "svelte";
|
import { createEventDispatcher } from "svelte";
|
||||||
export let opened_from;
|
export let opened_from;
|
||||||
export let passed_org;
|
export let passed_org;
|
||||||
export let passed_orgs;
|
export let passed_orgs;
|
||||||
export let passed_team;
|
export let passed_team;
|
||||||
|
export let current_runners;
|
||||||
export let import_modal_open;
|
export let import_modal_open;
|
||||||
$: searchvalue = "";
|
$: searchvalue = "";
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher();
|
||||||
@@ -29,7 +34,16 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
let orgs = [];
|
||||||
|
RunnerOrganizationService.runnerOrganizationControllerGetAll().then((val) => {
|
||||||
|
orgs = val;
|
||||||
|
});
|
||||||
|
let teams = [];
|
||||||
|
RunnerTeamService.runnerTeamControllerGetAll().then((val) => {
|
||||||
|
teams = val;
|
||||||
|
});
|
||||||
let selected_org;
|
let selected_org;
|
||||||
|
$: selected_org_or_team = "";
|
||||||
let files;
|
let files;
|
||||||
let recent_processed = true;
|
let recent_processed = true;
|
||||||
$: json_output = [];
|
$: json_output = [];
|
||||||
@@ -118,6 +132,52 @@
|
|||||||
recent_processed = true;
|
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>
|
</script>
|
||||||
@@ -192,6 +252,24 @@
|
|||||||
</select>
|
</select>
|
||||||
<p>{$_('bitte-bestaetige-diese-laeufer-fuer-den-import')}</p>
|
<p>{$_('bitte-bestaetige-diese-laeufer-fuer-den-import')}</p>
|
||||||
{/if}
|
{/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'}
|
{#if opened_from === 'OrgDetail'}
|
||||||
<p>
|
<p>
|
||||||
{$_('runnerimport_verify_runners_org', {
|
{$_('runnerimport_verify_runners_org', {
|
||||||
@@ -225,7 +303,7 @@
|
|||||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
{$_('csv_import__lastname')}
|
{$_('csv_import__lastname')}
|
||||||
</th>
|
</th>
|
||||||
{#if opened_from !== 'TeamDetail'}
|
{#if (opened_from !== 'TeamDetail' && opened_from !== 'RunnerOverview') || (opened_from === 'RunnerOverview' && selected_org_or_team.includes('ORG_'))}
|
||||||
<th
|
<th
|
||||||
scope="col"
|
scope="col"
|
||||||
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
@@ -250,7 +328,7 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
{runner[`${$_('csv_import__lastname')}`]}
|
{runner[`${$_('csv_import__lastname')}`]}
|
||||||
</td>
|
</td>
|
||||||
{#if opened_from !== 'TeamDetail'}
|
{#if (opened_from !== 'TeamDetail' && opened_from !== 'RunnerOverview') || (opened_from === 'RunnerOverview' && selected_org_or_team.includes('ORG_'))}
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
{runner[`${$_('csv_import__team')}`] || runner[`${$_('csv_import__class')}`] || '---'}
|
{runner[`${$_('csv_import__team')}`] || runner[`${$_('csv_import__class')}`] || '---'}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
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 { _ } from "svelte-i18n";
|
||||||
import StatCards from "./StatCards.svelte";
|
import StatCards from "./StatCards.svelte";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
import ComponentDump from "./ComponentDump.svelte";
|
|
||||||
let navOpen = false;
|
let navOpen = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -21,5 +20,4 @@
|
|||||||
👋</span>
|
👋</span>
|
||||||
</h1>
|
</h1>
|
||||||
<StatCards />
|
<StatCards />
|
||||||
<ComponentDump />
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
on:cancelDelete={(event) => {
|
on:cancelDelete={(event) => {
|
||||||
import_modal_open = false;
|
import_modal_open = false;
|
||||||
}}
|
}}
|
||||||
|
current_runners={[]}
|
||||||
passed_team={{}}
|
passed_team={{}}
|
||||||
passed_orgs={[]}
|
passed_orgs={[]}
|
||||||
passed_org={orgdata}
|
passed_org={orgdata}
|
||||||
|
|||||||
@@ -78,8 +78,7 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<div
|
<div class="text-sm font-medium text-gray-900">
|
||||||
class="text-sm font-medium text-gray-900">
|
|
||||||
{o.name}
|
{o.name}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -88,8 +87,7 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<div
|
<div class="text-sm font-medium text-gray-900">
|
||||||
class="text-sm font-medium text-gray-900">
|
|
||||||
{#if o.address}
|
{#if o.address}
|
||||||
{JSON.stringify(o.address)}
|
{JSON.stringify(o.address)}
|
||||||
{:else}no address specified{/if}
|
{:else}no address specified{/if}
|
||||||
@@ -100,10 +98,13 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<div
|
<div class="text-sm font-medium text-gray-900">
|
||||||
class="text-sm font-medium text-gray-900">
|
|
||||||
{#if o.contact}
|
{#if o.contact}
|
||||||
{JSON.stringify(o.contact)}
|
<a
|
||||||
|
href="../contacts/{o.contact.id}"
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{o.contact.firstname}
|
||||||
|
{o.contact.middlename || ''}
|
||||||
|
{o.contact.lastname}</a>
|
||||||
{:else}no contact specified{/if}
|
{:else}no contact specified{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -47,5 +47,6 @@
|
|||||||
passed_org={{}}
|
passed_org={{}}
|
||||||
passed_orgs={current_organizations}
|
passed_orgs={current_organizations}
|
||||||
opened_from="OrgOverview"
|
opened_from="OrgOverview"
|
||||||
|
current_runners={[]}
|
||||||
bind:import_modal_open />
|
bind:import_modal_open />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
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 />
|
||||||
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}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
import AddRunnerModal from "./AddRunnerModal.svelte";
|
import AddRunnerModal from "./AddRunnerModal.svelte";
|
||||||
import ImportRunnerModal from "./ImportRunnerModal.svelte";
|
import ImportRunnerModal from "./ImportRunnerModal.svelte";
|
||||||
import RunnersOverview from "./RunnersOverview.svelte";
|
import RunnersOverview from "./RunnersOverview.svelte";
|
||||||
let current_runners = [];
|
$: current_runners = [];
|
||||||
export let modal_open = false;
|
export let modal_open = false;
|
||||||
export let import_modal_open = false;
|
export let import_modal_open = false;
|
||||||
</script>
|
</script>
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
passed_team={{}}
|
passed_team={{}}
|
||||||
passed_orgs={[]}
|
passed_orgs={[]}
|
||||||
passed_org={{}}
|
passed_org={{}}
|
||||||
|
bind:current_runners
|
||||||
opened_from="RunnerOverview"
|
opened_from="RunnerOverview"
|
||||||
bind:import_modal_open />
|
bind:import_modal_open />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,14 +1,41 @@
|
|||||||
<script>
|
<script>
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
import { RunnerService } from "@odit/lfk-client-js";
|
import { RunnerService,RunnerTeamService,
|
||||||
|
RunnerOrganizationService, } from "@odit/lfk-client-js";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
import RunnersEmptyState from "./RunnersEmptyState.svelte";
|
import RunnersEmptyState from "./RunnersEmptyState.svelte";
|
||||||
|
import Select from 'svelte-select';
|
||||||
$: searchvalue = "";
|
$: searchvalue = "";
|
||||||
$: active_deletes = [];
|
$: active_deletes = [];
|
||||||
export let current_runners = [];
|
export let current_runners = [];
|
||||||
const runners_promise = RunnerService.runnerControllerGetAll().then((val) => {
|
const runners_promise = RunnerService.runnerControllerGetAll().then((val) => {
|
||||||
current_runners = 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>
|
</script>
|
||||||
|
|
||||||
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')}
|
{#if store.state.jwtinfo.userdetails.permissions.includes('RUNNER:GET')}
|
||||||
@@ -29,6 +56,12 @@
|
|||||||
placeholder={$_('datatable.search')}
|
placeholder={$_('datatable.search')}
|
||||||
aria-label={$_('datatable.search')}
|
aria-label={$_('datatable.search')}
|
||||||
class="gridjs-input gridjs-search-input mb-4" />
|
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
|
<div
|
||||||
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
class="shadow border-b border-gray-200 sm:rounded-lg overflow-x-scroll">
|
||||||
<table class="divide-y divide-gray-200 w-full">
|
<table class="divide-y divide-gray-200 w-full">
|
||||||
@@ -61,11 +94,9 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody class="divide-y divide-gray-200">
|
<tbody class="divide-y divide-gray-200">
|
||||||
{#each current_runners as runner}
|
{#each current_runners as runner}
|
||||||
{#if Object.values(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)}
|
||||||
.toString()
|
{#if filterGroupIDs.includes(runner.group.id)||filterGroupIDs.includes(runner.group.parentGroup?.id)||filterGroupIDs.length===0}
|
||||||
.toLowerCase()
|
<tr data-rowid="user_{runner.id}" data-groupid={runner.group.id}>
|
||||||
.includes(searchvalue)}
|
|
||||||
<tr data-rowid="user_{runner.id}">
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
@@ -86,7 +117,16 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
{runner.group.name}
|
{#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>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">{runner.distance}</td>
|
<td class="px-6 py-4 whitespace-nowrap">{runner.distance}</td>
|
||||||
{#if active_deletes[runner.id] === true}
|
{#if active_deletes[runner.id] === true}
|
||||||
@@ -129,6 +169,7 @@
|
|||||||
</td>
|
</td>
|
||||||
{/if}
|
{/if}
|
||||||
</tr>
|
</tr>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -74,6 +74,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ImportRunnerModal
|
<ImportRunnerModal
|
||||||
|
current_runners={[]}
|
||||||
on:cancelDelete={(event) => {
|
on:cancelDelete={(event) => {
|
||||||
import_modal_open = false;
|
import_modal_open = false;
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -80,8 +80,7 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<div
|
<div class="text-sm font-medium text-gray-900">
|
||||||
class="text-sm font-medium text-gray-900">
|
|
||||||
{t.name}
|
{t.name}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -90,10 +89,11 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<div
|
<div class="text-sm font-medium text-gray-900">
|
||||||
class="text-sm font-medium text-gray-900">
|
|
||||||
{#if t.parentGroup}
|
{#if t.parentGroup}
|
||||||
{t.parentGroup.name}
|
<a
|
||||||
|
href="../orgs/{t.parentGroup.id}"
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{t.parentGroup.name}</a>
|
||||||
{:else}no organization specified{/if}
|
{:else}no organization specified{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -102,10 +102,13 @@
|
|||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<div
|
<div class="text-sm font-medium text-gray-900">
|
||||||
class="text-sm font-medium text-gray-900">
|
|
||||||
{#if t.contact}
|
{#if t.contact}
|
||||||
{JSON.stringify(t.contact)}
|
<a
|
||||||
|
href="../contacts/{t.contact.id}"
|
||||||
|
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">{t.contact.firstname}
|
||||||
|
{t.contact.middlename || ''}
|
||||||
|
{t.contact.lastname}</a>
|
||||||
{:else}no contact specified{/if}
|
{:else}no contact specified{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
import lodashIsEqual from "lodash.isequal";
|
import lodashIsEqual from "lodash.isequal";
|
||||||
import store from "../store";
|
import store from "../store";
|
||||||
|
import isEmail from "validator/es/lib/isEmail";
|
||||||
import { UserService, UserGroupService } from "@odit/lfk-client-js";
|
import { UserService, UserGroupService } from "@odit/lfk-client-js";
|
||||||
import Toastify from "toastify-js";
|
import Toastify from "toastify-js";
|
||||||
import PromiseError from "./PromiseError.svelte";
|
import PromiseError from "./PromiseError.svelte";
|
||||||
@@ -72,7 +73,8 @@
|
|||||||
$: groups_changed =
|
$: groups_changed =
|
||||||
JSON.stringify(usergroups_array) ===
|
JSON.stringify(usergroups_array) ===
|
||||||
JSON.stringify(usergroups_array_original);
|
JSON.stringify(usergroups_array_original);
|
||||||
$: save_enabled = changes_performed || !groups_changed;
|
$: save_enabled =
|
||||||
|
(changes_performed || !groups_changed) && isEmail(editable_userdata.email);
|
||||||
function submit() {
|
function submit() {
|
||||||
if (data_loaded === true && save_enabled) {
|
if (data_loaded === true && save_enabled) {
|
||||||
editable_userdata.groups = usergroups_array;
|
editable_userdata.groups = usergroups_array;
|
||||||
@@ -263,6 +265,10 @@
|
|||||||
name="email"
|
name="email"
|
||||||
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" />
|
class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm rounded-l-md sm:text-sm border-gray-300 border bg-gray-50 text-gray-500 dark:bg-gray-900 dark:text-gray-100 rounded-md p-2" />
|
||||||
</div>
|
</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">
|
<div class="text-sm w-full">
|
||||||
<label
|
<label
|
||||||
for="username"
|
for="username"
|
||||||
|
|||||||
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,165 +1,182 @@
|
|||||||
{
|
{
|
||||||
"404message": "Sorry, the page you are looking for could not be found.",
|
"404message": "Sorry, the page you are looking for could not be found.",
|
||||||
"404title": "Error 404",
|
"404title": "Error 404",
|
||||||
"about": "About",
|
"about": "About",
|
||||||
"action": "Action",
|
"action": "Action",
|
||||||
"add-your-first-track": "Add your first track",
|
"add-your-first-track": "Add your first track",
|
||||||
"address": "Address",
|
"address": "Address",
|
||||||
"application_name": "Lauf für Kaya! - Admin",
|
"application_name": "Lauf für Kaya! - Admin",
|
||||||
"author": "Author",
|
"author": "Author",
|
||||||
"bitte-bestaetige-diese-laeufer-fuer-den-import": "Please confirm these runners for import",
|
"bitte-bestaetige-diese-laeufer-fuer-den-import": "Please confirm these runners for import",
|
||||||
"browse": "Browse",
|
"browse": "Browse",
|
||||||
"by": "by",
|
"by": "by",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"cannot-reset-your-password-directly": "Bummer. We unfortunately cannot reset your password directly. Please send us a mail and confirm your identity",
|
"cannot-reset-your-password-directly": "Bummer. We unfortunately cannot reset your password directly. Please send us a mail and confirm your identity",
|
||||||
"changelog": "Changelog",
|
"changelog": "Changelog",
|
||||||
"close": "Close",
|
"close": "Close",
|
||||||
"confirm-delete": "Confirm Delete",
|
"confirm-delete": "Confirm Delete",
|
||||||
"confirm-deletion": "Confirm Deletion",
|
"confirm-deletion": "Confirm Deletion",
|
||||||
"contact": "Contact",
|
"contact": "Contact",
|
||||||
"contact-information": "Contact Information",
|
"contact-information": "Contact Information",
|
||||||
"count_organizations": "# Organizations",
|
"count_organizations": "# Organizations",
|
||||||
"count_teams": "# Teams",
|
"count_teams": "# Teams",
|
||||||
"create": "Create",
|
"create": "Create",
|
||||||
"create-a-new-runner": "Create a new Runner",
|
"create-a-new-runner": "Create a new Runner",
|
||||||
"create-a-new-track": "Create a new Track",
|
"create-a-new-track": "Create a new Track",
|
||||||
"create-organization": "Create Organization",
|
"create-organization": "Create Organization",
|
||||||
"create-user": "Create User",
|
"create-user": "Create User",
|
||||||
"credits": "Credits",
|
"credits": "Credits",
|
||||||
"csv_import__class": "Class",
|
"csv_import__class": "Class",
|
||||||
"csv_import__firstname": "Firstname",
|
"csv_import__firstname": "Firstname",
|
||||||
"csv_import__lastname": "Lastname",
|
"csv_import__lastname": "Lastname",
|
||||||
"csv_import__middlename": "Middlename",
|
"csv_import__middlename": "Middlename",
|
||||||
"csv_import__team": "Team",
|
"csv_import__team": "Team",
|
||||||
"dashboard-greeting": "hello there",
|
"dashboard-greeting": "hello there",
|
||||||
"dashboard-title": "Dashboard",
|
"dashboard-title": "Dashboard",
|
||||||
"datatable": {
|
"datatable": {
|
||||||
"search": "🔍 Search...",
|
"search": "🔍 Search...",
|
||||||
"sort_column_ascending": "Sort column ascending",
|
"sort_column_ascending": "Sort column ascending",
|
||||||
"sort_column_descending": "Sort column descending",
|
"sort_column_descending": "Sort column descending",
|
||||||
"previous": "Previous",
|
"previous": "Previous",
|
||||||
"next": "Next",
|
"next": "Next",
|
||||||
"page": "Page",
|
"page": "Page",
|
||||||
"showing": "Showing",
|
"showing": "Showing",
|
||||||
"records": "Records",
|
"records": "Records",
|
||||||
"of": "of",
|
"of": "of",
|
||||||
"to": "to",
|
"to": "to",
|
||||||
"loading": "Loading...",
|
"loading": "Loading...",
|
||||||
"no_matching_records_found": "No matching records found",
|
"no_matching_records_found": "No matching records found",
|
||||||
"an_error_happened_while_fetching_the_data": "An error happened while fetching the data"
|
"an_error_happened_while_fetching_the_data": "An error happened while fetching the data"
|
||||||
},
|
},
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"delete-organization": "Delete Organization",
|
"delete-organization": "Delete Organization",
|
||||||
"delete-runner": "Delete Runner",
|
"delete-runner": "Delete Runner",
|
||||||
"delete-team": "Delete Team",
|
"delete-team": "Delete Team",
|
||||||
"delete-user": "Delete User",
|
"delete-user": "Delete User",
|
||||||
"dependency_name": "Name",
|
"dependency_name": "Name",
|
||||||
"distance": "Distance",
|
"distance": "Distance",
|
||||||
"distance-in-km": "Distance in km",
|
"distance-in-km": "Distance in km",
|
||||||
"dont-have-your-email-connected": "Don't have your email connected?",
|
"dont-have-your-email-connected": "Don't have your email connected?",
|
||||||
"dont-panic-were-resetting-it": "Don't panic, we're resetting it ✌",
|
"dont-panic-were-resetting-it": "Don't panic, we're resetting it ✌",
|
||||||
"drag-and-drop-your-files-or": "Drag & Drop your files or",
|
"drag-and-drop-your-files-or": "Drag & Drop your files or",
|
||||||
"e-mail-adress": "E-Mail Adress",
|
"e-mail-adress": "E-Mail Adress",
|
||||||
"edit-permissions": "edit permissions",
|
"edit-permissions": "edit permissions",
|
||||||
"email_address_or_username": "Email / username",
|
"email_address_or_username": "Email / username",
|
||||||
"error_on_login": "Error on login",
|
"error_on_login": "Error on login",
|
||||||
"faq": "FAQ",
|
"faq": "FAQ",
|
||||||
"filepond__abort": "Abort",
|
"filepond__abort": "Abort",
|
||||||
"filepond__cancel": "Cancel",
|
"filepond__cancel": "Cancel",
|
||||||
"filepond__error-during-load": "Error during load",
|
"filepond__error-during-load": "Error during load",
|
||||||
"filepond__error-during-remove": "Error during remove",
|
"filepond__error-during-remove": "Error during remove",
|
||||||
"filepond__error-during-revert": "Error during revert",
|
"filepond__error-during-revert": "Error during revert",
|
||||||
"filepond__error-during-upload": "Error during upload",
|
"filepond__error-during-upload": "Error during upload",
|
||||||
"filepond__field-contains-invalid-files": "Field contains invalid files",
|
"filepond__field-contains-invalid-files": "Field contains invalid files",
|
||||||
"filepond__loading": "Loading",
|
"filepond__loading": "Loading",
|
||||||
"filepond__remove": "Remove",
|
"filepond__remove": "Remove",
|
||||||
"filepond__retry": "Retry",
|
"filepond__retry": "Retry",
|
||||||
"filepond__size-not-available": "Size not available",
|
"filepond__size-not-available": "Size not available",
|
||||||
"filepond__tap-to-cancel": "tap to cancel",
|
"filepond__tap-to-cancel": "tap to cancel",
|
||||||
"filepond__tap-to-retry": "tap to retry",
|
"filepond__tap-to-retry": "tap to retry",
|
||||||
"filepond__tap-to-undo": "tap to undo",
|
"filepond__tap-to-undo": "tap to undo",
|
||||||
"filepond__undo": "Undo",
|
"filepond__undo": "Undo",
|
||||||
"filepond__upload": "Upload",
|
"filepond__upload": "Upload",
|
||||||
"filepond__upload-cancelled": "Upload cancelled",
|
"filepond__upload-cancelled": "Upload cancelled",
|
||||||
"filepond__upload-complete": "Upload complete",
|
"filepond__upload-complete": "Upload complete",
|
||||||
"filepond__uploading": "Uploading",
|
"filepond__uploading": "Uploading",
|
||||||
"filepond__waiting-for-size": "Waiting for size",
|
"filepond__waiting-for-size": "Waiting for size",
|
||||||
"first-name": "First name",
|
"first-name": "First name",
|
||||||
"first-name-is-required": "First Name is required",
|
"first-name-is-required": "First Name is required",
|
||||||
"forgot_password?": "Forgot your password?",
|
"forgot_password?": "Forgot your password?",
|
||||||
"general-stats": "General Stats",
|
"general-stats": "General Stats",
|
||||||
"general_promise_error": "😢 Error",
|
"general_promise_error": "😢 Error",
|
||||||
"goback": "Go Home",
|
"go-to-login": "Go To Login",
|
||||||
"group": "Group",
|
"goback": "Go Home",
|
||||||
"groups": "Groups",
|
"group": "Group",
|
||||||
"hallo": "hello",
|
"groups": "Groups",
|
||||||
"icon-image-credits": "We also want to thank these projects for illustrations and icons:",
|
"hallo": "hello",
|
||||||
"import-runners": "Import runners",
|
"icon-image-credits": "We also want to thank these projects for illustrations and icons:",
|
||||||
"import__target-organization": "Target Organization",
|
"import-runners": "Import runners",
|
||||||
"installed-version": "Installed version",
|
"import__target-organization": "Target Organization",
|
||||||
"invalid-mail-reset": "the provided email is invalid",
|
"imprint": "Imprint 🧾",
|
||||||
"last-name": "Last name",
|
"imprint-loading": "Imprint loading...",
|
||||||
"last-name-is-required": "Last Name is required",
|
"installed-version": "Installed version",
|
||||||
"lfk-is-os": "The \"Lauf für Kaya!\" Frontend is (like all other projects for the \"LfK!\" Also) an open source project.",
|
"invalid-mail-reset": "the provided email is invalid",
|
||||||
"license": "License",
|
"last-name": "Last name",
|
||||||
"licenses-are-being-loaded": "Licenses are being loaded...",
|
"last-name-is-required": "Last Name is required",
|
||||||
"loading-runners": "loading runners...",
|
"lfk-is-os": "The \"Lauf für Kaya!\" Frontend is (like all other projects for the \"LfK!\" Also) an open source project.",
|
||||||
"log_in": "Log in",
|
"license": "License",
|
||||||
"log_in_to_your_account": "Log in to your account",
|
"licenses-are-being-loaded": "Licenses are being loaded...",
|
||||||
"login_is_checked": "Login is being checked...",
|
"loading-runners": "loading runners...",
|
||||||
"logout": "Logout",
|
"log_in": "Log in",
|
||||||
"mail-validation-in-progress": "mail validation in progress...",
|
"log_in_to_your_account": "Log in to your account",
|
||||||
"manage-admin-users": "manage admin users",
|
"login_is_checked": "Login is being checked...",
|
||||||
"middle-name": "Middle name",
|
"logout": "Logout",
|
||||||
"minimum-lap-time-in-s": "minimum lap time in s",
|
"mail-validation-in-progress": "mail validation in progress...",
|
||||||
"name": "Name",
|
"manage-admin-users": "manage admin users",
|
||||||
"no-license-text-could-be-found": "No license text could be found 😢",
|
"middle-name": "Middle name",
|
||||||
"no-tracks-added-yet": "there are no tracks added yet.",
|
"minimum-lap-time-in-s": "minimum lap time in s",
|
||||||
"organization": "Organization",
|
"name": "Name",
|
||||||
"organizations": "Organizations",
|
"new-password": "New password",
|
||||||
"orgs": "Orgs",
|
"no-license-text-could-be-found": "No license text could be found 😢",
|
||||||
"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!",
|
"no-tracks-added-yet": "there are no tracks added yet.",
|
||||||
"password": "Password",
|
"organization": "Organization",
|
||||||
"password-is-required": "Password is required",
|
"organizations": "Organizations",
|
||||||
"permissions": "Permissions",
|
"orgs": "Orgs",
|
||||||
"phone": "Phone",
|
"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!",
|
||||||
"please-provide-the-required-csv-xlsx-file": "Please provide the required csv/ xlsx file",
|
"password": "Password",
|
||||||
"please-provide-the-required-information-to-add-a-new-runner": "Please provide the required information to add a new runner.",
|
"password-is-required": "Password is required",
|
||||||
"please-provide-the-required-information-to-add-a-new-track": "Please provide the required information to add a new track.",
|
"password-reset-failed": "Password reset failed!",
|
||||||
"profile-picture": "Profile Picture",
|
"password-reset-in-progress": "Password Reset in Progress...",
|
||||||
"read-license": "Read License",
|
"password-reset-successful": "Password Reset successful!",
|
||||||
"register": "Register",
|
"permissions": "Permissions",
|
||||||
"repo_link": "Link",
|
"phone": "Phone",
|
||||||
"reset-my-password": "Reset my password",
|
"please-provide-a-password": "Please provide a password...",
|
||||||
"runner-import": "Runner Import",
|
"please-provide-the-required-csv-xlsx-file": "Please provide the required csv/ xlsx file",
|
||||||
"runner-updated": "Runner updated!",
|
"please-provide-the-required-information-to-add-a-new-runner": "Please provide the required information to add a new runner.",
|
||||||
"runnerimport_verify_runners_org": "Please confirm these runners for import into the organization \"{org_name}\"",
|
"please-provide-the-required-information-to-add-a-new-track": "Please provide the required information to add a new track.",
|
||||||
"runners": "Runners",
|
"please-request-a-new-reset-mail": "Please request a new reset mail...",
|
||||||
"save-changes": "Save Changes",
|
"privacy": "Privacy 🔒",
|
||||||
"send-a-mail-to-lfk-odit-services": "send a mail to lfk@odit.services",
|
"privacy-loading": "Privacy loading...",
|
||||||
"settings": "Settings",
|
"profile-picture": "Profile Picture",
|
||||||
"signout": "Sign out",
|
"read-license": "Read License",
|
||||||
"stats-are-being-loaded": "stats are being loaded...",
|
"register": "Register",
|
||||||
"team": "Team",
|
"repo_link": "Link",
|
||||||
"team-name": "Team name",
|
"request-a-new-reset-mail": "Request a new reset mail",
|
||||||
"teams": "Teams",
|
"reset-my-password": "Reset my password",
|
||||||
"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...",
|
"reset-password": "Reset your password",
|
||||||
"this-might-take-a-moment": "This might take a moment 👀",
|
"runner-import": "Runner Import",
|
||||||
"total-distance": "total distance",
|
"runner-updated": "Runner updated!",
|
||||||
"total-donations": "total donations",
|
"runnerimport_verify_runners_org": "Please confirm these runners for import into the organization \"{org_name}\"",
|
||||||
"total-scans": "total scans",
|
"runners": "Runners",
|
||||||
"track-added": "Track added",
|
"save-changes": "Save Changes",
|
||||||
"track-data-is-being-loaded": "Track data is being loaded",
|
"send-a-mail-to-lfk-odit-services": "send a mail to lfk@odit.services",
|
||||||
"track-is-being-added": "Track is being added...",
|
"settings": "Settings",
|
||||||
"track-length-in-m": "Track Length in m",
|
"signout": "Sign out",
|
||||||
"track-name": "Track name",
|
"stats-are-being-loaded": "stats are being loaded...",
|
||||||
"tracks": "Tracks",
|
"successful-password-reset": "Successful password reset!",
|
||||||
"updating-runner": "Updating runner...",
|
"team": "Team",
|
||||||
"updating-user": "updating user...",
|
"team-name": "Team name",
|
||||||
"user-updated": "User updated",
|
"teams": "Teams",
|
||||||
"username": "Username",
|
"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...",
|
||||||
"users": "Users",
|
"this-might-take-a-moment": "This might take a moment 👀",
|
||||||
"valid-email-is-required": "valid email is required",
|
"total-distance": "total distance",
|
||||||
"welcome_wavinghand": "Welcome 👋",
|
"total-donations": "total donations",
|
||||||
"your_profile": "Your Profile"
|
"total-scans": "total scans",
|
||||||
|
"track-added": "Track added",
|
||||||
|
"track-data-is-being-loaded": "Track data is being loaded",
|
||||||
|
"track-is-being-added": "Track is being added...",
|
||||||
|
"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",
|
||||||
|
"contacts": "Contacts",
|
||||||
|
"create-a-new-contact": "Create a new contact"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user